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

Вопрос

Насколько я понял, база данных 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 . "'')");
// разъединение

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

No responses yet

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