Темы
Уязвимость безопасности адресных баз данных
Чтобы повысить безопасность в Dreamweaver 8.0.2 и CS3, мы удалили возможность использования динамических параметров SQL внутри стандартных наборов записей. Первоначальный код делал базы данных уязвимыми к внедрению SQL-кода. Использование предварительно подготовленных операторов для моделей серверов ASP_VBS и ColdFusion позволило устранить уязвимость системы безопасности. При применении первого набора записей необходимо было добавить функцию на страницу, а затем вызвать эту функцию из всех наборов записей на странице для PHP_MySQL. Этот метод аналогичен предварительно подготовленным операторам ASP_VBS и ColdFusion.
Для устранения уязвимости безопасности нам пришлось пожертвовать гибкостью. Мы хотим предоставить разработчикам, которые уже имеют или планируют разрабатывать расширения, в которых будет выполняться обработка динамических SQL-запросов, а также конечным пользователям, которые хотят настроить свои наборы записей для работы с динамическими параметрами, возможность редактировать пользовательские наборы записей из Dreamweaver.
Внедрение SQL-кода и методы его предотвращения
Внедрение SQL-кода — это метод, использующий уязвимость безопасности, возникающую на уровне базы данных приложения. Уязвимость проявляется в том случае, когда пользовательский ввод либо неправильно отфильтрован для экранирующих символов строкового литерала, встроенных в SQL-операторы, либо не является строго типизированным и поэтому неожиданно выполняется. По сути, это пример более общего класса уязвимостей, которые могут возникать всякий раз, когда один язык программирования или язык сценариев встроен в другой. Дополнительная информации приведена в Wikipedia.
Пример стандартного SQL-запроса:
SELECT * FROM company_com WHERE name_com = '$companyName'
В приведенном выше примере переменная $companyName считывается из поля формы ввода, поэтому пользователь полностью контролирует, какое значение будет отправлено. Так, в штатной ситуации, когда пользователь вводит «Adobe» (без кавычек) в поле ввода, соответствующем названию компании, SQL-запрос будет выглядеть следующим образом:
SELECT * FROM company_com WHERE name_com = 'Adobe'
Но бывают случаи, когда злоумышленники хотят взломать веб-сайт и украсть конфиденциальную информацию и/или уничтожить базу данных. В вышеприведенном примере внедрение SQL-кода легко выполнимо посредством ввода следующей строки: "blabla' OR '1'='1"(без окружающих кавычек). С помощью этой последовательности символов злоумышленник получит доступ не к одной, а ко всем компаниям. Фактически выполняемый код SQL выглядит следующим образом:
SELECT * FROM company_com WHERE name_com = 'blabla' OR '1'='1'
Более того, если злоумышленник хочет удалить всю таблицу company_com, он может сделать это, просто отправив следующую строку "x'; DROP TABLE company_com; --"(без окружающих кавычек). Так выглядит полный запрос SQL:
SELECT * FROM company_com WHERE name_com = 'x'; DROP TABLE company_com; --'
Для защиты вашего сайта от внедрения SQL-кода используйте одну из следующих мер:
- Ограничьте ввод минимальным набором разрешенных символов, автоматически удаляя все символы, выходящие за пределы заданного диапазона.
- Запретите использование всех символов, которые могут вызвать проблемы при использовании внутри SQL-запроса (например, одинарные кавычки).
- Используйте предварительно подготовленные операторы.
- Используйте права доступа к базе данных.
- Используйте хранимые процедуры.
Мы рекомендуем использовать подготовленные операторы, подробно описанные в документе Использование предварительно подготовленных операторов. В программах Dreamweaver 8.0.2 и CS3 предварительно подготовленные операторы используются для моделей серверов ASP_VBS, ASP_JavaScript, Cold Fusion и JSP, а экранирование пользовательского ввода — для модели сервера PHP_MySQL. Дополнительные сведения о внедрении SQL-кода приведены в документе Атаки с внедрением SQL-кода на примерах.
Использование предварительно подготовленных операторов
В программах Dreamweaver 8.0.2 и CS предварительно подготовленные операторы используются потому, что это решение гарантирует замену фактических параметров SQL на заданные значения (возможно, отправленные через URL) на сервере, а не на странице. Кроме того, применение предварительно подготовленных операторов гарантирует, что значение, переданное самому SQL, имеет соответствующий тип данных и использует соответствующее экранирование (если параметр имеет тип int, то пользователь не сможет отправить письмо; если параметр имеет тип text, то вся переданная строка будет правильно экранирована, и у пользователя не будет возможности выполнить внедрение SQL-кода).
Предварительно подготовленные операторы для моделей серверов ASP_VBS и ASP_JS
Для ASP_VBS и ASP_JS мы используем уровень базы данных ADODB. В следующем примере показан простой набор записей ASP_VBS, в котором динамический параметр SQL используется для фильтрации результатов:
... <% Dim Recordset1__MMColParam Recordset1__MMColParam = "1" If (Request.QueryString("id_com") <> "") Then Recordset1__MMColParam = Request.QueryString("id_com") End If %> <% Dim Recordset1 Dim Recordset1_cmd Dim Recordset1_numRows Set Recordset1_cmd = Server.CreateObject ("ADODB.Command") Recordset1_cmd.ActiveConnection = MM_connContacts_STRING Recordset1_cmd.CommandText = "SELECT * FROM company_com WHERE id_com = ?" Recordset1_cmd.Prepared = true Recordset1_cmd.Parameters.Append Recordset1_cmd.CreateParameter("param1", 5, 1, -1, Recordset1__MMColParam) ' adDouble Set Recordset1 = Recordset1_cmd.Execute Recordset1_numRows = 0 %> ...Предварительно подготовленные операторы для модели сервера ColdFusion
ColdFusion предлагает встроенную поддержку для предварительно подготовленных операторов. В следующем примере показан простой набор записей ColdFusion, в котором динамический параметр SQL используется для фильтрации результатов:
... <cfparam name="URL.id_com" default="1"> <cfquery name="Recordset1" datasource="company_employee"> SELECT * FROM company_com WHERE id_com = <cfqueryparam value="#URL.id_com#" cfsqltype="cf_sql_numeric"> </cfquery> ...Смоделированные подготовленные операторы для модели сервера PHP_MySQL
Предварительно подготовленные операторы для PHP встроены в MySQL 4.1, но в целях обеспечения поддержки и предыдущих версий MySQL была реализована функция, выполняющая аналогичное действие. В следующем примере показан простой набор записей PHP, в котором динамический параметр SQL используется для фильтрации результатов:
... <?php if (!function_exists("GetSQLValueString")) { function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") { $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue; $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue); switch ($theType) { case "text": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "long": case "int": $theValue = ($theValue != "") ? intval($theValue) : "NULL"; break; case "double": $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL"; break; case "date": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "defined": $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue; break; } return $theValue; } } $colname_Recordset1 = "-1"; if (isset($_GET['id_com'])) { $colname_Recordset1 = $_GET['id_com']; } mysql_select_db($database_connContacts, $connContacts); $query_Recordset1 = sprintf("SELECT * FROM company_com WHERE id_com = %s", GetSQLValueString($colname_Recordset1, "int")); $Recordset1 = mysql_query($query_Recordset1, $connContacts) or die(mysql_error()); $row_Recordset1 = mysql_fetch_assoc($Recordset1); $totalRows_Recordset1 = mysql_num_rows($Recordset1); ?> ...Разрешение динамических параметров в стандартных наборах записей
Итак, как защититься от внедрения SQL-кода, используя динамические параметры и сохраняя результирующий код редактируемым через панель «Варианты поведения сервера» в Dreamweaver? Решение состоит в том, чтобы вставить динамические параметры в сам запрос SQL, а не передавать их как классические параметры SQL (которые могут быть либо экранированы, либо добавлены в SQL с помощью подготовленных операторов).
Важно. Пользователи не могут генерировать такой код, используя панели «Объекты» и «Варианты поведения сервера», которые поставляются с Dreamweaver 8.0.2 или CS3. (Пример: вставка набора записей через панель «Варианты поведения сервера» или строку «Вставка».) Это решение требует от пользователя умения редактирования кода вручную или установки стороннего расширения, создающего такой код. Ответственность за написание безопасного кода ложится на пользователя или стороннего разработчика программного обеспечения.
Цель данного метода — дать возможность опытным разработчикам создавать динамические SQL-запросы. Неправильное использование данного решения позволит хакерам получить нежелательные привилегии и/или взломать веб-страницы и базу данных. Чтобы свести к минимуму риск внедрения, обратитесь к разделу Внедрение SQL-кода и методы его предотвращения.
Основные случаи, которые данное решение будет подходящим:
- Ручное создание/обновление SQL-запроса для принятия динамических параметров.
- Создание/обновление существующих расширений для генерации динамических параметров.
Оба варианта будут подробно описаны в следующих разделах.
Создание/обновление SQL-запроса вручную для принятия динамических параметров при сохранении возможности редактирования набора записей из Dreamweaver
В следующем примере пользователь хочет добавить в динамическую таблицу функцию сортировки. Он решает перезагрузить страницу, передавая файлы и направление сортировки, которое будет использоваться (по возрастанию или по убыванию) в URL-адресе. Пример исходного URL:
http://www.mydomain.com/index.php?sortCol=name_com&sortDir=ascДля выполнения этой задачи в Dreamweaver требуется кодирование вручную с использованием панели «Варианты поведения сервера». В следующих примерах кода показаны изменения, которые необходимо внести для каждой модели сервера.
Важно. Обратите внимание, что ни один из приведенных ниже примеров не защищен от внедрения SQL-кода. Они приведены здесь исключительно с образовательной целью и не должны использоваться в производстве. Это довольно простые примеры, сделанные для максимально легкого понимания. Вам необходимо защитить свою код от внедрения SQL-кода самостоятельно. Дополнительные сведения приведены в разделе Внедрение SQL-кода и методы его предотвращения.
ASP_VBS
До:
... <% Dim Recordset1 Dim Recordset1_cmd Dim Recordset1_numRows Set Recordset1_cmd = Server.CreateObject ("ADODB.Command") Recordset1_cmd.ActiveConnection = MM_connContacts_STRING Recordset1_cmd.CommandText = "SELECT * FROM company_com" Recordset1_cmd.Prepared = true Set Recordset1 = Recordset1_cmd.Execute Recordset1_numRows = 0 %> ...После:
... <% Dim orderBy: orderBy = "" If (Request.QueryString("sortCol") <> "") Then orderBy = "ORDER BY " & Request.QueryString("id_com") If (Request.QueryString("sortDir") <> "") Then orderBy = orderBy & " " & Request.QueryString("sortDir") End If End If %> <% Dim Recordset1 Dim Recordset1_cmd Dim Recordset1_numRows Set Recordset1_cmd = Server.CreateObject ("ADODB.Command") Recordset1_cmd.ActiveConnection = MM_connContacts_STRING Recordset1_cmd.CommandText = "SELECT * FROM company_com " & orderBy & "" Recordset1_cmd.Prepared = true Set Recordset1 = Recordset1_cmd.Execute Recordset1_numRows = 0 %> ...ColdFusion
До:
... <cfquery name="Recordset1" datasource="company_employee"> SELECT * FROM company_com </cfquery> ...После:
... <cfparam name="URL.sortCol" default=""> <cfparam name="URL.sortDir" default="ASC"> <cfset orderBy=""> <cfif (#URL.sortCol# NEQ "")> <cfset orderBy="ORDER BY #URL.sortCol# #URL.sortDir#"> </cfif> <cfquery name="Recordset1" datasource="company_employee"> SELECT * FROM company_com #orderBy# </cfquery> ...PHP_MySQL
До:
... <?php mysql_select_db($database_connContacts, $connContacts); $query_Recordset1 = "SELECT * FROM company_com"; $Recordset1 = mysql_query($query_Recordset1, $connContacts) or die(mysql_error()); $row_Recordset1 = mysql_fetch_assoc($Recordset1); $totalRows_Recordset1 = mysql_num_rows($Recordset1); ?> ...После:
... <?php $orderBy = ""; if (isset($_GET['sortCol'])) { $orderBy = "ORDER BY " . $_GET['sortCol']; if (isset($_GET['sortDir'])) { $orderBy .= " " . $_GET['sortDir']; } } ?> <?php mysql_select_db($database_connContacts, $connContacts); $query_Recordset1 = "SELECT * FROM company_com " . $orderBy . ""; $Recordset1 = mysql_query($query_Recordset1, $connContacts) or die(mysql_error()); $row_Recordset1 = mysql_fetch_assoc($Recordset1); $totalRows_Recordset1 = mysql_num_rows($Recordset1); ?> ...Создание/обновление существующих расширений для генерации динамических параметров при сохранении возможности редактирования набора записей из Dreamweaver
Теперь рассмотрим тот же сценарий, что и выше, за исключением того, что пользователь хочет создать расширение, которое будет генерировать соответствующий код, а не полагаться на комбинацию встроенной функции и ручного редактирования. Новое расширение должно генерировать правильный код для моделей серверов ASP_VBS, ColdFusion и PHP_MySQL.
Важно. В следующих разделах мы сосредоточимся только на создании кода, который для создания динамического запроса SQL будет использовать значения параметров URL. Основное внимание уделяется генерации кода, который будет распознаваться Dreamweaver и редактирование которого будет возможно из стандартных интерфейсов Dreamweaver. Данное учебное пособие не подходит для проверки данных ввода (через URL-параметры) или защиты окончательного SQL от любых попыток внедрения SQL-кода, так как сложность такого кода выходит за рамки данного учебного пособия. Разработчик несет полную ответственность за защиту окончательного продукта от внедрения SQL-кода.
Мы предоставляем демонстрационное расширение, которое принимает соединение и таблицу в качестве входных данных и генерирует динамическую таблицу, отображающую все записи. Оно также обновляет сгенерированный SQL, чтобы тот содержал переменную orderBy, как показано в примерах выше. Расширение было разработано для Dreamweaver 8.0.2 и CS3. Сгенерированный код не является слишком надежным, так как его целью является создание наборов записей, содержащих динамические параметры, которые остаются редактируемыми с использованием стандартных интерфейсов Dreamweaver. Исходный код расширения находится в Папке конфигурации пользователя.
Во всех приведенных ниже примерах кода выделенные разделы были добавлены для включения функции сортировки с помощью параметров URL-адресов sortCol и sortDir.
ASP_VBS
Код, который генерирует соответствующий SQL-запрос для модели сервера ASP_VPS, находится в файле «[ПАПКА_КОНФИГУРАЦИИ_ПОЛЬЗОВАТЕЛЯ]/Commands/My Dynamic Table.js" в Папке конфигурации пользователя. Важное изменение в строке 130:
... 130: paramObj.encodedSQL = "SELECT * FROM " + paramObj.table + " \" & orderBy & \""; ...Файл участников для варианта поведения сервера MyDynamicTable ASP_VBS включает в себя дополнительного участника, который добавляет определение переменной orderBy на страницу:
<group name="MyDynamicTable" version="9.0"> <groupParticipants> <groupParticipant name="connectionref_statement" /> <groupParticipant name="MyDynamicTable_orderBy" /> <groupParticipant name="recordset_main" /> <groupParticipant name="repeatedRegion_init2" /> <groupParticipant name="DynamicTable_main" /> <groupParticipant name="recordset_close" /> </groupParticipants> </group>ColdFusion
Аналогичный файл «[ПАПКА_КОНФИГУРАЦИИ_ПОЛЬЗОВАТЕЛЯ]/Commands/My Dynamic Table.js» в Папке конфигурации пользователя содержит также код для модели сервера ColdFusion; соответствующее изменение в данном случае находится в строке 138:
... 138: paramObj.SQLStatement = "SELECT * FROM " + paramObj.table + " #orderBy#"; ...Файл участников для варианта поведения сервера MyDynamicTable ColdFusion включает в себя дополнительного участника, который добавляет определение переменной orderBy на страницу:
<group name="MyDynamicTable" version="9.0"> <groupParticipants> <groupParticipant name="MyDynamicTable_orderBy" /> <groupParticipant name="Recordset_main" /> <groupParticipant name="DynamicTable_main" /> <groupParticipant name="RepeatedRegion_pageNum" /> <groupParticipant name="RepeatedRegion_maxRows" /> <groupParticipant name="RepeatedRegion_startRow" /> <groupParticipant name="RepeatedRegion_endRow" /> <groupParticipant name="RepeatedRegion_totalPages" /> </groupParticipants> </group>PHP_MySQL
Последняя модель сервера в расширении — PHP_MySQL. Соответствующее изменение в файле «[ПАПКА_КОНФИГУРАЦИИ_ПОЛЬЗОВАТЕЛЯ]/Commands/My Dynamic Table.js" в Папке конфигурации пользователя находится в строке 122:
... 122: paramObj.SQLStatement = "SELECT * FROM " + paramObj.table+ " \" . $orderBy . \""; ...Файл участников для варианта поведения сервера MyDynamicTable PHP_MySQL включает в себя дополнительного участника, который добавляет определение переменной orderBy на страницу:
<group name="MyDynamicTable" version="9.0"> <groupParticipants> <groupParticipant name="Connection_include" /> <groupParticipant name="MyDynamicTable_orderBy" /> <groupParticipant name="EditOps_SQLValueString" /> <groupParticipant name="Recordset_main" /> <groupParticipant name="DynamicTable_main" /> <groupParticipant name="Recordset_close" /> </groupParticipants> </group>Плюсы
Описанный выше метод имеет несколько преимуществ, а именно:
- Более опытные разработчики могут воспользоваться уязвимостью к внедрению SQL-кода для динамического построения более сложных SQL.
- Созданные наборы записей будут по-прежнему доступны для редактирования из Dreamweaver.
- Нетрудно интегрировать код в существующие расширения Dreamweaver, которые генерируют наборы записей с динамическими параметрами SQL.
- Код по умолчанию, сгенерированный Dreamweaver, остается неизменным; разработчики, которым не требуются динамические запросы (и их клиенты), могут быть уверены в безопасности кода, написанного Dreamweaver.
Минусы
Поскольку Dreamweaver не был разработан по умолчанию для поддержки этого метода, он также имеет и некоторые недостатки, а именно:
- Код, сгенерированный с использованием метода, описанного в этом документе, уязвим для внедрения SQL-кода и поэтому должен быть защищен другими способами. Ниже приведены примеры способов защиты кода (это, разумеется, неполный список):
- Убедитесь, что конечный пользователь не может использовать метод внедрения SQL-кода для создания веб-страницы и/или базы данных;
- Экранируйте объекты, прежде чем они достигнут реального SQL;
- Убедитесь, что даже если пользователю удастся взломать ваш код, он не сможет украсть конфиденциальную информацию и/или базу данных.
- Две известные проблемы, которые невозможно устранить до выхода следующей версии Dreamweaver:
- ColdFusion: простой пользовательский интерфейс набора записей удаляет хэши вокруг имен переменных из SQL запроса при редактировании и повторном применении
- ColdFusion, ASP_VBS, ASP_JS: кнопка «Тест» в расширенном интерфейсе набора записей не работает, когда запрос SQL содержит имена переменных
Нахождение папки конфигурации пользователя
- Windows XP
C:\Documents and Settings\[имя пользователя]\Application Data\Adobe\Dreamweaver 9\Configuration
- Windows Vista
C:\Users\[имя пользователя]\AppData\Roaming\Adobe\Dreamweaver 9\Configuration
- Mac OS 10.4.x
/Users/[имя пользователя]/Library/Application\ Support/Adobe/Dreamweaver\ 9/Configuration