Как предотвратить SQL-инъекции в PHP?

MySQL, PHP, Вопросы и ответы
18 февраля 2012

Вопрос

Насколько я понял, база данных MySQL особо уязвима от SQL-инъекций. Вы не могли бы объяснить мне, как можно защититься от них? Предположим, есть вот такой код:

// соединяемся с базой
$unsafe_variable = $_POST["user-input"];
mysql_query("INSERT INTO table (column) VALUES ('" . $unsafe_variable . "')");
// разъединяемся

Что надо изменить, чтобы быть уверенным на 100%, что SQL-инъекция не пройдёт?


Ответ №1

Способ 1. Запросы и параметрами

Используйте так называемые готовые выражения (prepared statements) и запросы с параметрами (параметризованные запросы - parameterized queries). MySQL поддерживает такой способ передачи запросов, при котором сами команды SQL посылаются и обрабатываются сервером отдельно от параметров.

Если Вы используете PHP Dara Objects, Вы можете работать с готовыми выражениями как-то так:

$preparedStatement = $db->prepare('SELECT * FROM employees WHERE name = :name');
$preparedStatement->execute(array(':name' => $name));
$rows = $preparedStatement->fetchAll();

Подробнее смотрите в документации по PDO. Класс mysqli также позволяет использовать запросы с параметрами.

Что же происходит в этом случае при передачи SQL-выражения в базу данных? Оно анализируется и обрабатывается. С помощью специфических параметров (типа «?» или «:name» в примере выше) Вы указываете движку базы данных, что именно необходимо отфильтровать. Затем, когда Вы вызываете команду «выполнить», готовое выражение объединяется со значением параметров, которое Вы задали.

Важный момент здесь - это то, что значения параметров объединяются с откомпилированными выражениями, а не из SQL-строкой. SQL-инъекции сводятся к внедрению вредоносного фрагмента в сам SQL-запрос, который создаётся перед отправкой в базу данных. Так что отделение команд SQL от параметров сводит к минимуму риск возникновения непредвиденных ситуаций. Любые параметры, которые Вы посылаете, используя готовые выражения, будут восприниматься как строка (несмотря на то, что база данных может проводить над ними необходимую оптимизацию). В примере выше, если переменная $name будет содержать

'Sarah'; DELETE * FROM employees

, то результат будет просто поиском по строке

"'Sarah'; DELETE * FROM employees"

, и Вы, естественно, в итоге не останетесь с пустой таблицей.

Ещё одна польза от использования готовых выражений - это то, что Вы можете удобно выполнять одни и те же команды несколько раз за одну сессию, и они будут обрабатываются и компилироваться всего один раз. Это может дать небольшой прирост производительности.

Поскольку Вы спросили, как можно реализовать защиту от инъекций на примере вставки, вот Вам пример:

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');
$preparedStatement->execute(array(':column' => $unsafeValue));

Способ 2. Экранирование строк

Есть и другой, более «короткий» способ защититься от SQL-инъекций. Он сводится к экранированию всех динамических данных, которые будут использоваться в запросе. Экранирование заменяет кавычки на символы «\"», апостроф на «\'», а обратную косую черту на «\\». Это позволяет предотвратить внедрение вредоносного фрагмента в то место, где ожидаются данные. Делается это с помощью функции mysql_real_escape_string(). Ваш пример будет иметь вид:

// соединение
$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);
mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
// разъединение

З.Ы. Касательно перевода терминологии не уверен.

Нет комментариев

Добавить комментарий


(обязательно)