Фильтрация данных, поступающих со стороны пользователя, с точки зрения безопасности самый важный компонент любого веб-приложения. Сегодня я хотел бы рассказать какие основные составляющие по-моему мнению должна включать правильная обработка входящих данных. Первое и самое главное правило – все, что приходит от пользователя или каким-либо образом может быть им изменено, должно проходить обязательную проверку на стороне сервера. Однако всеми излюбленный метод, при котором ко всем элементам массивов GET, POST и Cookie применяется addslashes(), не является абсолютно безопасным. Пример такого кода:
<?php if(!get_magic_quotes_gpc()) { $_GET = array_map('addslashes', $_GET); $_POST = array_map('addslashes', $_POST); $_COOKIE = array_map('addslashes', $_COOKIE); } ?>
Дело в том, что, если в каком-либо запросе присутствует переменная, которую мы можем изменить, и она взята без кавычек, то даже экранирование кавычек не поможет:
<?php include('connect.php'); //производит соединение с БД и содержит код выше $blah=$_GET['blah']; mysql_query("SELECT * FROM foo WHERE bar=$blah LIMIT 1"); ?>
Не смотря на включенный magic_quotes_gpc, такой запрос подвержен SQL-инъекции, так как никаких лишних кавычек нам закрывать не надо, а при реализации инъекции нам кавычки вообще не нужны. Таким образом, все значения, которые участвуют в условиях после WHERE, необходимо обрамлять кавычками.
Хорошим тоном безопасного кода является предварительное инициализирование переменных, в которые будут записаны данные, поступающие со стороны пользователя. Некоторые разработчики, не задумываясь, используют функции extract() и import_request_variables() для помещения элементов и их значений из $_GET, $_POST и $_COOKIE в суперглобальный массив $GLOBALS, что ведет к произвольному инициализированию и перезаписи ранее инициализированных переменных. Забудьте про $GLOBALS и опцию register_globals! В PHP6 этого уже не будет, и переменных “без ресурса” мы больше не увидим, так как это пережитки эпохи третьего PHP. Каждый скрипт должен знать какие именно входящие переменные к нему поступают и откуда они поступают.
Весь функционал, ответственный за импортирование переменных из массивов GPC и обработку их значений, должен быть реализован в одной конкретной функции. Назовем ее к примеру import_var(). Первый аргумент данной функции – это имя переменной (одно или несколько). Он должен представлять собой массив, так как у разработчика должна быть возможность инициализировать сразу несколько однотипных переменных. Второй аргумент – это имя массива, откуда будет импортирована эта переменная. Можно использовать первые буквы – G, P и C. Третий – это тип данной переменной. В зависимости от поля применения переменных, типов может быть много:
- INT числовое значение
- NUM числовое значение, включающее числа с плавающей запятой
- TEXT строковое значение
- ALPHA строковое значение, допускающее только буквенные и числовые символы
- MAIL строковое значение, представляющее собой email адрес
- ARR массив
- BOOL булева
Для каждого типа переменной должен быть предусмотрен отдельный способ фильтрации. Например для NUM – это проверка с помощью функции is_numeric, для INT – is_numeric и is_float, для MAIL – проверка с помощью регулярного выражения и т.д.
Четвертый аргумент – это максимальная длина значения. Отдельный аргумент необходим для этой цели, так как строковые значения могут иметь разную длину. Таким образом, прототип нашей функции будет иметь следующий вид:
import_var((array)$name,(string)$source,(string)$type,(int)maxlen=0)
Пример подобной функции я встретил в Seditio CMS, однако до версии 121 она была реализована не самым лучшим образом, так как некорректно обрабатывала массивы, что привело к обнаружению мной уязвимости в данном движке.
Leave a Reply