Как предотвратить SQL-инъекции в PHP?
Описание проблемы
Я столкнулся с проблемой SQL-инъекций в своем приложении. Когда пользовательский ввод вставляется в SQL-запрос без предварительной обработки, это делает систему уязвимой к атакам. Я привожу ниже пример кода, где данная уязвимость имеется:
$unsafe_variable = $_POST['user_input'];
mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");
В этом примере пользователь может ввести такой текст, как value'); DROP TABLE table;--
, и запрос превратится в следующий:
INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')
Это позволяет злоумышленникам выполнять произвольные SQL-команды, что может привести к потере данных или другим нежелательным последствиям.
Как я могу защитить свое приложение от подобных уязвимостей? Какие лучшие практики могут помочь предотвратить SQL-инъекции?
4 ответ(ов)
Как бы вы ни поступили, обязательно проверьте, не была ли ваша входная информация уже нарушена magic_quotes
или другим подобным "добрым" инструментом. Если это так, вам, возможно, придется обработать ее с помощью stripslashes
или другого подходящего метода для очистки данных.
Если возможно, приводите типы ваших параметров. Однако это работает только с простыми типами, такими как int, bool и float.
$unsafe_variable = $_POST['user_id'];
$safe_variable = (int)$unsafe_variable;
mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
В этом примере мы приводим переменную $unsafe_variable
, полученную из массива $_POST
, к целочисленному типу с помощью (int)
. Это помогает предотвратить некоторые типы атак, такие как SQL-инъекции, так как мы гарантируем, что в запросе используется именно целое число. Тем не менее, рекомендуется также использовать подготовленные выражения для повышения безопасности при работе с базой данных.
Вопросы о PHP и MySQL действительно очень распространены, но вот пример кода для PHP и Oracle, который показывает, как предотвратить SQL-инъекции и использовать драйверы oci8:
$conn = oci_connect($username, $password, $connection_string);
$stmt = oci_parse($conn, 'UPDATE table SET field = :xx WHERE ID = 123');
oci_bind_by_name($stmt, ':xx', $fieldval);
oci_execute($stmt);
Этот код использует подготовленные выражения и связывает параметры, что помогает защитить ваш код от SQL-инъекций. Также не забудьте обрабатывать возможные ошибки соединения и выполнения запросов для повышения надежности вашего приложения.
Вы написали функцию sqlvprintf
, которая позволяет формировать SQL-запросы с использованием синтаксиса, похожего на String.Format
в C#. Вот основной функционал вашей функции:
function sqlvprintf($query, $args)
{
global $DB_LINK;
$ctr = 0;
ensureConnection(); // Подключаемся к базе данных, если еще не подключены.
$values = array();
foreach ($args as $value)
{
if (is_string($value))
{
$value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'";
}
else if (is_null($value))
{
$value = 'NULL';
}
else if (!is_int($value) && !is_float($value))
{
die('Допускаются только числовые, строковые, массивы и NULL аргументы в запросе. Аргумент '.($ctr+1).' является некорректным типом, его тип: '. gettype($value). '.');
}
$values[] = $value;
$ctr++;
}
$query = preg_replace_callback(
'/{(\\d+)}/',
function($match) use ($values)
{
if (isset($values[$match[1]]))
{
return $values[$match[1]];
}
else
{
return $match[0];
}
},
$query
);
return $query;
}
function runEscapedQuery($preparedQuery /*, ...*/)
{
$params = array_slice(func_get_args(), 1);
$results = runQuery(sqlvprintf($preparedQuery, $params)); // Выполняем запрос и получаем результаты.
return $results;
}
Это позволяет выполнять запросы в одной строке, например:
runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);
Функция экранирует данные с учётом типа переменной. Однако если вы попытаетесь параметризировать имена таблиц или столбцов, это не сработает, так как все строки помещаются в кавычки, что ведет к синтаксической ошибке.
Важно отметить, что в предыдущей версии, использовавшей str_replace
, была возможность инъекций за счет добавления токенов в пользовательские данные. Однако текущая версия с использованием preg_replace_callback
не создает таких проблем, если в замещении присутствуют эти токены.
Как санировать пользовательский ввод с помощью PHP?
UTF-8 на всех уровнях!
SQL: выбрать только строки с максимальным значением в столбце
Как вывести сырой SQL-запрос в виде строки из билдера запросов?
Когда использовать одинарные кавычки, двойные кавычки и обратные кавычки в MySQL