Защитите серверные модули PHP от уязвимости SQL-инъекций

Последнее обновление 27 апр. 2021 г.

Проблема

Некоторые базы данных позволяют отправлять несколько операторов SQL в одном запросе. Поэтому существуют потенциальные риски безопасности при передаче параметров в строке запроса к динамически создаваемому запросу базы данных.Хакеры могут попытаться изменить переменные URL или формы в динамическом запросе, добавив вредоносные SQL-операторы к существующим параметрам.Это часто называют атакой с внедрением SQL. Некоторые коды поведения сервера, созданные Dreamweaver, для снижения риска таких атак должны быть изменены. Более подробная информация о внедрении SQL-кода приведена в статье Википедии. (См. статью 201713)

Примечание. Проблема, описанная в данном техническом документе, была исправлена в Dreamweaver 8.0.2 Updater.

Данный технический документ касается серверного поведения PHP в Dreamweaver.Существуют отдельные технические документы для серверного поведения ColdFusion, ASP VBScript, ASP JavaScript и JSP.Серверное поведение ASP.NET не затронуто.

Решение

Когда вы позволяете строке запроса передавать параметр, убедитесь в том, что передается только ожидаемая информация. Adobe создала Dreamweaver 8.0.2 Updater, которое снижает риск атак SQL-инъекций.Эти исправления будут включены во все будущие выпуски Dreamweaver.После обновления Dreamweaver 8 вам нужно будет повторно применить серверное поведение к страницам, которые его используют, и повторно развернуть эти страницы на вашем сервере.

В оставшейся части данного технического документа описано, как вручную отредактировать код Dreamweaver MX 2004 для предотвращения атак SQL-инъекций с моделью сервера PHP.Серверное поведение, уязвимое к этим атакам, перечислено ниже вместе с исправлениями:

Набор записей с фильтром

Наборы записей без фильтрации не требуется изменять, в отличие от наборов с фильтром. В примере ниже набор записей Dreamweaver MX 2004 под названием «rs_byDate» отфильтрован по значению даты, переданному из параметра URL.Выделенный ниже код — это то, что нужно изменить:

<?php $colname_rs_byDate = "1"; if (isset($_GET['relDate'])) { $colname_rs_byDate = (get_magic_quotes_gpc()) ? $_GET['relDate'] : addslashes($_GET['relDate']); } mysql_select_db($database_dreamqa2, $dreamqa2); $query_rs_byDate = sprintf("SELECT * FROM albums WHERE relDate = '%s'", $colname_rs_byDate); $rs_byDate = mysql_query($query_rs_byDate, $dreamqa2) or die(mysql_error()); $row_rs_byDate = mysql_fetch_assoc($rs_byDate); $totalRows_rs_byDate = mysql_num_rows($rs_byDate); ?>

Чтобы защитить набор записей от атак SQL-инъекций, следует создать пользовательскую функцию GetSQLValueString() в верхней части страницы.Эта функция проверяет тип данных параметра запроса.После создания функции обновите код набора записей для использования функции GetSQLValueString().

Перейдите ко второй строке в режиме кода, сразу после включаемого файла для соединения, и добавьте функцию GetSQLValueString() в начало кода следующим образом:

<?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; } } ?>

Удалите код magic quotes, чтобы обратные слеши не добавлялись некорректно к данным GET и POST, когда magic quotes отключены на PHP-сервере.

Исходный код:

$colname_rs_byDate = (get_magic_quotes_gpc()) ? $_GET['relDate'] : addslashes($_GET['relDate']);

Измененный код:

$colname_rs_byDate = $_GET['relDate'];



Обновите код набора записей для создания SQL-строки с помощью функции GetSQLValueString(), созданной выше. Обновите значение переменной $query_rs_byDate:

Исходный код:

$query_rs_byDate = sprintf("SELECT * FROM albums WHERE relDate = '%s'", $colname_rs_byDate);

Измененный, безопасный код:

$query_rs_byDate = sprintf("SELECT * FROM albums WHERE relDate = %s", GetSQLValueString($colname_rs_byDate, "date"));

Поведения сервера «Вставить запись», «Обновить запись» и «Удалить запись» и мастер «Форма вставки записи»

Поведения сервера «Вставить запись», «Обновить запись» и «Удалить запись» и мастер «Форма вставки записи» уже используют функцию GetSQLValueString(), поэтому их нужно доработать для предотвращения SQL-инъекций. Единственные необходимые изменения находятся в функции GetSQLValueString().

Вот исходный код. Строка, которую нужно изменить, выделена желтым:

if (!function_exists("GetSQLValueString")) { function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") {$theValue = (!get_magic_quotes_gpc()) ? addslashes($theValue) : $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; } }

Обновите определение переменной $theValue:

Исходный код:

$theValue = (!get_magic_quotes_gpc()) ? addslashes($theValue) : $theValue;

Измененный защищенный код:

$theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue; $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue);

Поведение сервера «Вход пользователя в систему»

Выполните следующие действия, чтобы исправить поведение сервера входа пользователя:

Создайте функцию GetSQLValueString(), как описано в разделе Набор записей с фильтром выше.

Измените код, который создает SQL-запрос входа.



Исходный код:

$LoginRS__query=sprintf("SELECT username, password FROM login WHERE username='%s' AND password='%s'", get_magic_quotes_gpc() ? $loginUsername : addslashes($loginUsername), get_magic_quotes_gpc() ? $password : addslashes($password));

Измененный защищенный код:

$LoginRS__query=sprintf("SELECT username, password FROM login WHERE username=%s AND password=%s", GetSQLValueString($loginUsername, "text"), GetSQLValueString($password, "text"));

Поведение сервера «Проверить новое имя пользователя»

Поведение сервера «Проверить новое имя пользователя» требует, чтобы вначале на страницу было добавлено поведение «Вставить запись». Функция GetSQLValuesString() включена в поведение сервера «Вставить запись». Необходимо только обновить GetSQLValuesString(), чтобы лучше обрабатывать SQL-инъекции, и обновить поведение сервера «Проверить нового пользователя», чтобы использовать эту функцию при передаче параметра.

Измените первую строку в функции, чтобы обновить функцию GetSQLValuesString(), как описано в разделе Обновить определение переменной $theValue.

Измените код для построения SQL-запроса входа, чтобы использовать GetSQLValuesString() для передачи параметров. В верхней части страницы найдите код в разделе, начинающемся с // *** Redirect if username exists.



Исходный код:

$LoginRS__query = "SELECT username FROM login WHERE username='" . $loginUsername . "'"



Измененный защищенный код:

$LoginRS__query = sprintf("SELECT username FROM login WHERE username=%s", GetSQLValueString($loginUsername, "text"));

Основной набор страниц сведений

Для объекта приложения «Основной набор страниц сведений» изменения касаются в основном набора записей, которые Dreamweaver создает для страницы сведений. Если на главной странице нет фильтруемого набора записей, то никаких изменений в главную страницу вносить не нужно. Если у вас есть отфильтрованный набор записей, см. раздел набор записей с фильтром, чтобы обновить код набора записей.

Dreamweaver создает отфильтрованный набор записей на странице сведений, поэтому код этого набора записей необходимо обновить, чтобы использовать функцию GetSQLValueString() для передачи параметров и sprintf() для построения строки SQL.

Создайте функцию GetSQLValueString(), как описано в разделе Набор записей с фильтром выше.

Обновите код объявления переменной и построите SQL-запрос, используя функцию GetSQLValuesString().



Исходный код:

$recordID = $_GET['recordID']; $query_DetailRS1 = "SELECT * FROM albums WHERE ID = $recordID";



Измененный защищенный код:

$colname_DetailRS1 = "-1"; if (isset($_GET['recordID'])) { $colname_DetailRS1 = (get_magic_quotes_gpc()) ? $_GET['recordID'] : addslashes($_GET['recordID']); } $query_DetailRS1 = sprintf("SELECT * FROM albums WHERE ID = %s", GetSQLValueString($colname_DetailRS1, "int"));

Мастер формы обновления записи

Обновите код для набора записей, который требуется для формы обновления записи, как описано в шаге 2 набора записей с фильтром.

Дополнительная информация

Для получения дополнительной информации об SQL-инъекциях обратитесь к следующей статье: статья Wikipedia об SQL-инъекциях.

Бюллетень безопасности Adobe: APSB 06-07 Уязвимость SQL-инъекций в Server Behavior для Dreamweaver.