Фильтрация данных, поступающих со стороны пользователя, с точки зрения безопасности самый важный компонент любого веб-приложения. Сегодня я хотел бы рассказать какие основные составляющие по-моему мнению должна включать правильная обработка входящих данных. Первое и самое главное правило – все, что приходит от пользователя или каким-либо образом может быть им изменено, должно проходить обязательную проверку на стороне сервера. Однако всеми излюбленный метод, при котором ко всем элементам массивов 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 она была реализована не самым лучшим образом, так как некорректно обрабатывала массивы, что привело к обнаружению мной уязвимости в данном движке.
14 replies on “Как нужно проверять входящие данные”
как с тобой связаться можно?
стучи в ICQ 5o221o или пиши на email admin[at]raz0r.name
> (string)$source
– не думаю что хорошая идея. Фильтрации не происходит. Лучше использовать htmlspecialchars($source), например, при переменных которые скрипт получает через $_POST (сообщения, etc).
> (array)$name
Тоже не совсем хорошо. В некоторых случаях можно привести строковое значение в массив, например:
изначально – script.php?test=value
присваиваем тип array – script.php?test[]=value
Часто вызывает ошибку, позволяя раскрывать реальные пути, что нередко помогает при взломе.
Мое имхо, использовать регэкспы для этих целей (за исключением is_numeric, (num), тут все ясно)
Для полного счастья не хватает практических примеров как можно реально избавиться от инъекций в значения переменных. В mysql часто помогает mysql_escape_string или mysql_real_escape_string. Как я уже упоминал, htmspecialchars() полезная функция.
Ждем пост с примерами, думаю многим будет интересно 😉
c0nst, может быть я не очень понятно написал, но полного кода функции, которую я предлагаю использовать в моем посте нет – это всего лишь прототип, т.е. аргументы и какие типы данных они должны представлять собой. Прототип я привел для примера, чтобы понять как вызывать данную функцию:
Как уже писал, пример такой функции можно посмотреть в Seditio CMS
>Ждем пост с примерами, думаю многим будет интересно
Хорошо, попробую написать свою )
[…] дополнение к прошлому посту, и как подметил автор SecureBlog’а, хотел бы добавить […]
[…] уже не раз излагал свои мысли по поводу принципов грамотной фильтрации […]
[…] дополнение к прошлому посту, и как подметил автор SecureBlog’а, хотел бы добавить […]
[…] уже не раз излагал свои мысли по поводу принципов грамотной фильтрации […]
как сделать так, чтобы при неправильном введении параметра id редиректило на какую-то страницу?
А что за функция такая import_var ? Не нашел ее описания нигде =((
меня тоже интересует как проверить числовой ли параметр и сделать редирект если не числовой.
http://raz0r.name/releases/funkciya-dlya-obrabotki-vxodyashhix-dannyx/
Неосилил (( Тупо спрошу, подойдет ли она для того чтобы обработать числовую переменную $id и переменную например для определения имени странички news.php $pagename на содержание sql inj или XSS ?
вполне