Защита от SQL-инъекций

Отключаем "волшебные кавычки"

В настройках php, т.е в файле php.ini надо отключить следующее:
magic_quotes_gpc
magic_quotes_runtime

Если у вас нет доступа файлу php.ini отключить их можно в файле .htaccess:

php_flag magic_quotes_gpc 0
php_flag magic_quotes_runtime 0

Нам нужно их отключить, потому что они могут нам мешать. Начиная с версии 5.3.0 она считается устаревшей, а в 5.4.0 и вовсе удалена.

Правила составления запросов MySQL

Для предотвращения SQL инъекций следует соблюдать два простых правила:

  1. Не помещать в БД данные без обработки. Это можно сделать либо с помощью подготовленных выражений, либо обрабатывая параметры вручную. Во втором случае надо сделать следующее:
    - привести все числовые параметры к нужному типу;
    - все остальные параметры обрабатываем функцией mysql_real_escape_string() и заключаем в кавычки.
  2. Не помещать в запрос управляющие структуры и идентификаторы, введенные пользователем. А заранее прописывать в скрипте список возможных вариантов, и выбирать только из них.

При подстановке строковых данных в запрос нужно сделать следующее:
- имена полей и таблиц заключаем в обратные одинарные кавычки;
- все вставляемые строковые данные заключаем в кавычки (удобнее в одинарные);
- экранируем слешами спецсимволы.

#если user совпадет с ключевыми словами СУБД, то выдаст ошибку
SELECT * FROM users WHERE name = Anna
#CУБД может принять Anna как имя другого поля
SELECT * FROM `users` WHERE `name` = Anna
#Правильный вариант, если в строк нет кавычки
SELECT * FROM `users` WHERE `name` = 'Anna'
#СУБД решить что 'Жанна д' -это данные, а Арк - это команда
SELECT * FROM `users` WHERE `name` = 'Жанна д'Арк'
#Правильны вариант, если строка экранирована слешами
SELECT * FROM `users` WHERE `name` = 'Жанна д\'Арк'

Надо отметить, что добавленные слеши в базу НЕ идут. Они нужны только в запросе, а при попадании в базу слеши отбрасываются. Поэтому при получении данных из базы применять функцию stripslashes() не нужно.

Отключаем "Волшебные кавычки". Они добавляют слеши не там, где они нужны - при составлении запроса, а еще до попадания в скрипт! Также при использовании Unicode волшебные кавычки могут испортить весь текст. 

Для прослешивания используем функцию mysql_real_escape_string(). Во-первых, она облегчает ведение и чтение логов mysql, заменяя, например, символ перевода строки на "\n" и некоторые другие символы на escape-последовательности. Во-вторых, и самое главное - она корректно работает с многобайтными кодировками, принимая во внимание текущую кодировку MySQL и не портит, таким образом, тексты в кодировке Unicode.

Подготовленные выражения

Есть еще один способ отправлять запросы в БД, называемый "подготовленными выражениями" (prepared statements). Суть его заключается в том, что подготавливается шаблон запроса, со специальными маркерами, на место которых будут подставлены динамические компоненты.  Пример такого шаблона:

SELECT * FROM table WHERE name=?

Знак вопроса здесь - это тот самый маркер. По-другому он называетсй плейсхолдером (placeholder). Весь секрет в том, что данные на его место подставляет специальная функция, которая "привязывает" переменную к запросу. Вот как выглядит код в таком случае:

$stmt = $mysqli->prepare("SELECT District FROM City WHERE Name=?");
$stmt->bind_param("s", $city);
$stmt->execute();

В первой строчке мы подготавливаем шаблон запроса.
Во второй - привязываем к маркеру значение переменной $city.
В третьей строчке выполняем подготовленный таким образом запрос.
При этом запрос и данные идут в базу не вместе, а по отдельности, исключая возможность какой-либо ошибки или злонамеренной манипуляции.

Примерами являются MySQLi и PDO.

SQL Injection

Если требуется динамически подставлять в запрос операторы SQL или имена полей, баз данных, таблиц, то ни под каким видом не вставлять их в запрос напрямую. Все варианты таких добавлений должны быть ЗАРАНЕЕ прописаны в вашем скрипте и выбираться на основании того, что ввёл пользователь.

К примеру, если надо передать имя поля в оператор order by, то ни в коем случае нельзя подставлять его напрямую. Надо сначала проверить его. Например, сделать массив допустимых значений, и подставлять в запрос только если переданный параметр в этом массиве присутствует:

Мы ищем в массиве заранее описанных вариантов введённое пользователем слово, и, если находим, то выбираем соответствующий элемент массива. Если совпадения не будет найдено, то будет выбран первый элемент массива. Таким образом, в запрос подставляется не то, что ввёл пользователь, а то, что было прописано у нас в скрипте.

$orders=array("name","price","qty");
$key=array_search($_GET['sort'],$orders));
$orderby=$orders[$key];
$query="SELECT * FROM `table` ORDER BY $orderby";

Особенности работы с оператором LIKE

При работе с оператором LIKE существуют некоторые нюансы.
Во-первых, следует обратить внимание на то, что у этого оператора есть два своих спецсимвола - _ и %. Если вы не хотите, чтобы они использовались, как маски, а хотите искать буквальное совпадение с символами % и _, то их надо прослешить. Это можно сделать командой
$data = addCslashes($data, '%_');

Внимание - это не addslashes! В имени этой функции есть дополнительная буква "c".
Во-вторых, в силу некоторых причин в подставляемых в LIKE (и REGEXP) даных, надо удваивать слеши.
Следовательно, перед тем, как подставлять некую переменную в like, её надо отдельно обработать: либо прослешить только бэкслеш, если у нас уже стоят символы поиска по маске и мы хотим их использовать по назначению,
$data = addCslashes($data, '\\');

либо прослешить и символы поиска тоже, если мы хотим их добавить вручную,
$data = addCslashes($data, '\%_');

В итоге, код подготовки переменной для подстановки в LIKE может выглядеть так:
$data = '%'.addCslashes($data, '\%_').'%';

и полученное таким образом значение мы уже дальше можем подставлять в запрос, используя либо прослешивание, либо подстановку.

Ссылки

Wiki: Внедрение SQL-кода
Хабр: Профилактика SQL-инъекций
Хабр: Защита от SQL-инъекций в PHP и MySQL
Хабр: Выводы по SQL injection
Документация PHP: SQL-инъекции
Защита от SQL-инъекций

комментарии (0)