Валидация и очистка данных средствами PHP
Безопасность данных является очень важным моментом, который часто недооценивается как разработчиками, так и клиентами. Начиная с PHP 5.2.0 производить очистку и валидацию данных (проверку на соответствие определенным критериям) стало проще с введением фильтрации данных. Сегодня мы рассмотрим способы фильтрации, как использовать фильтры и создадим несколько пользовательских функций.
Введение
Я всегда чувствовал, что писать код на PHP легко, а еще легче писать на PHP плохой код. Широкому распространению PHP в сфере веб-разработки способствовали многие проекты с открытым исходным кодом (open-source) вроде WordPress, Drupal, Magento. Кроме того это и веб-приложения вроде Facebook и т.д. При столь широком применении PHP (динамические веб-сайты, платформы для блоггинга, системы управления контентом, использование в приложениях для электронной коммерции и др.) вероятность столкнуться с "грязной" информацией и небезопасными системами очень велика. Данное руководство покажет некоторые методы очистки и валидации данных с помощью PHP. Мы сфокусируем внимание на нескольких типах входных данных и на том, как использовать PHP-фильтры и пользовательские функции.
Зачем очищать и проверять?
В данном руководстве мы обратим внимание на информацию, поступающую непосредственно от пользователей, а также из других внешних источников. Это означает, что мы не контролируем информацию, которую мы получаем. Все, что мы можем - это контролировать то, что будет сделано с этой полученной информацией. Практически все виды угроз исходят от информации, передаваемой пользователями или другими третьими лицами.
— Регулярная проверка качества ссылок по более чем 100 показателям и ежедневный пересчет показателей качества проекта.
— Все известные форматы ссылок: арендные ссылки, вечные ссылки, публикации (упоминания, мнения, отзывы, статьи, пресс-релизы).
— SeoHammer покажет, где рост или падение, а также запросы, на которые нужно обратить внимание.
SeoHammer еще предоставляет технологию Буст, она ускоряет продвижение в десятки раз, а первые результаты появляются уже в течение первых 7 дней. Зарегистрироваться и Начать продвижение
Среди основных:
- XSS (Cross-Site Scripting - Межсайтовый скриптинг)
Это способ инъекции кода, когда скрипт внедряется в страницу атакуемого вебсайта с совершенно другого сайта на другом сервере. Эта уязвимость считается одной из самых распространенных в сети.
- SQL-инъекция
Следующей популярной уязвимостью является другая форма инъекции кода, которая позволяет реализовывать различные виды вредоносного поведения, включая несанкционированный доступ к информации, изменение информации в базе данных либо иное нарушения нормального функционирования веб-приложения. Осуществляется данная атака путем внедрения в запрос произвольного SQL-кода, предназначенного для взаимодействия с базой данных.
- CSRF/XSRF (Cross-Site Request Forgery - Подделка межсайтовых запросов)
Данная уязвимость менее распространенная по сравнению с предыдущими. Обычно такого рода уязвимости возникают при работе с сессиями и cookies и реже - при плохо проверенных и очищенных данных. CSRF может использоваться для выполнения сайтом каких либо запросов без ведома пользователя. Один из известных способов реализации данной атаки - использование неверно сформированного атрибута src у картинки, что приводит к выполнению какого-либо скрипта, а не к отображению картинки.
- Некорректная информация
Некорректная информация сама по себе не является "уязвимостью". Однако такая информация во многих случаях приводит к возникновению ряда проблем как для владельца сайта, так и для администратора баз данных. Зачастую некорректная по структуре информация приводит к нарушениям в работе, особенно если сайт реализован на любительском уровне, не по стандартам а также к сбоям в работе автоматизированных систем, ожидающих для обработки четко структурированные данные в определенном формате.

Перевод диалога к картинке:
- Здравствуйте, это из школы сына вас беспокоят. У нас тут неприятность с компьютерами.
- О Боже, он что, что-то сломал?
- Вашего сына действительно зовут Robert'); DROP TABLE students;?
- О, да, мы зовем его Little Bobby Tables
- Вы понимаете, мы потеряли все записи по ученикам этого года. Надеюсь, вы довольны.
- А я надеюсь, вы научитесь проверять заносимую в базу данных информацию.
Для наших целей мы будем фокусироваться только на использовании серверных методов повышения безопасности информации, поэтому давайте посмотрим, как определяются термины "sanitization" и "validation" применительно к PHP. Обратимся к руководству по PHP:
— Разгрузит мастера, специалиста или компанию;
— Позволит гибко управлять расписанием и загрузкой;
— Разошлет оповещения о новых услугах или акциях;
— Позволит принять оплату на карту/кошелек/счет;
— Позволит записываться на групповые и персональные посещения;
— Поможет получить от клиента отзывы о визите к вам;
— Включает в себя сервис чаевых.
Для новых пользователей первый месяц бесплатно. Зарегистрироваться в сервисе
"Валидация используется для проверки того, отвечает ли проверяемая информация определенным требованиям. Например, используя FILTER_VALIDATE_EMAIL мы определяем, является ли информация корректным (т.е. верным по структуре) e-mail-адресом, но не изменяем эти данные.
Очистка же подразумевает возможное изменение проверяемой информации, например - удаление нежелательных символов. Скажем, при использовании FILTER_SANITIZE_EMAIL будут удалены символы, которые не должны содержаться в e-mail-адресе. Т.е. в данном случае не происходит проверки корректности адреса (т.е. валидации), а удаляются заведомо неподходящие символы - не более того."
По сути, если представить ваш сайт как ночной клуб, в который каждый хочет попасть, валидация занимается проверкой наличия гостя в списке приглашенных, очистка (sanitization) выступает в роли вышибалы, который не допускает в клуб нежелательных элементов. Примерно так.
Какие фильтры есть у меня?
Все инсталляции PHP не могут быть идентичными. Несмотря на то, что фильтры были введены в PHP 5.2.0, не все инсталляции имеют одинаковый набор фильтров. В большинстве случаев все фильтры, о которых мы будем говорить, будут уже включены в установленный PHP на вашем сервере, но чтобы вы чуть больше знали о фильтрах, мы узнаем о том, что доступно именно на вашем сервере. В исходных кодах приложен файл getfilters.php, который, будучи однажды установленным и запущенным на сервере, отобразит список всех ваших фильтров (как фильтры информации, доступные через функцию filter_var, так и потоковые, доступные через stream_filter_append)
echo "<h1>Data Filters</h1>\n<table>\n<tr>\n"; echo "<td><strong>Filter ID</strong></td>\n"; echo "<td><strong>Filter Name</strong></td>\n</tr>"; foreach(filter_list() as $id =>$filter) { echo "<tr><td>$filter</td><td>".filter_id($filter)."</td></tr>\n"; } echo "</table>\n";
Сначала мы получаем массив, содержащий список всех доступных фильтров с помощью функции filter_list, после чего проходим циклом по массиву, выводя на экран имя фильтра и его ID.
Как мне использовать фильтр?
Фильтры PHP для валидации и очистки активируются посредством передачи функции filter_var как минимум двух параметров. В качестве примера давайте применим фильтр очистки для целого числа:
$value = '123abc456def'; echo filter_var($value, FILTER_SANITIZE_NUMBER_INT);
В этом примере у нас есть переменная value, которую мы передаем функции filter_var из расширения PHP Filters Extension, используя фильтр FILTER_SANITIZE_NUMBER_INT. В качестве результата мы получим:
123456
Фильтр очистки для целых числе удаляет все символы, не являющиеся целыми числами, выдавая нам "чистое" целое число. В исходниках вы можете попробовать различные входные данные, и к ним будет применяться ряд общих фильтров. В архив включены различные строки, которые вы можете использовать в качестве тестового материала самостоятельно.
Что делают различные фильтры?
Список ниже неполный, но он содержит большинство фильтров, которые идут в стандартной установке PHP 5.2.0+.
FILTER_VALIDATE_BOOLEAN: Проверяет, является ли переданная информация булевым значением TRUE или FALSE. Если переданное значение - значение не типа Boolean, то возвращается FALSE. Скрипт ниже выведет TRUE для примера с переменной value1 и FALSE для примера с переменной value02:
$value01 = TRUE; if(filter_var($value01,FILTER_VALIDATE_BOOLEAN)) { echo 'TRUE'; } else { echo 'FALSE'; } echo '<br /><br />' $value02 = FALSE; if(filter_var($value02,FILTER_VALIDATE_BOOLEAN)) { echo 'TRUE'; } else { echo 'FALSE'; }
FILTER_VALIDATE_EMAIL: Проверяет, является ли переданная информация корректным с точки зрения структуры e-mail адресом. Она не проверяет, существует ли этот адрес на самом деле, а только валидность адреса, т.е. правильность его структуры. Скрипт ниже выведет TRUE для примера с переменной value01 и FALSE для примера с переменной value02 (так как не хватает обязательной части со знаком @):
$value01 = 'test@example.com'; if(filter_var($value01,FILTER_VALIDATE_EMAIL)) { echo 'TRUE'; } else { echo 'FALSE'; } echo '<br /><br />' $value02 = 'nettuts'; if(filter_var($value02,FILTER_VALIDATE_EMAIL)) { echo 'TRUE'; } else { echo 'FALSE'; }
FILTER_VALIDATE_FLOAT: Проверяет, является ли переданное значение числом с плавающей точкой. Скрипт ниже выведет TRUE для примера с переменной value01 и FALSE для примера с переменной value02 (так как разделить "," не разрешен в числах с плавающей точкой):
$value01 = '1.234'; if(filter_var($value01,FILTER_VALIDATE_FLOAT)) { echo 'TRUE'; } else { echo 'FALSE'; } echo '<br /><br />' $value02 = '1,234'; if(filter_var($value02,FILTER_VALIDATE_FLOAT)) { echo 'TRUE'; } else { echo 'FALSE'; }
FILTER_VALIDATE_INT: Проверяет, является ли переданное значение целым числом. Скрипт ниже выведет TRUE для примера с переменной value01 и FALSE для примера с переменной value02 (десятичные числа не являются целыми):
$value01 = '123456'; if(filter_var($value01,FILTER_VALIDATE_INT)) { echo 'TRUE'; } else { echo 'FALSE'; } echo '<br /><br />' $value02 = '123.456'; if(filter_var($value02,FILTER_VALIDATE_INT)) { echo 'TRUE'; } else { echo 'FALSE'; }
FILTER_VALIDATE_IP: Проверяет, является ли переданное значение корректным IP-адресом. Она не проверяет, есть ли ответ от этого адреса, а лишь то, что переданное значение по своей структуре является IP-адресом. Скрипт ниже выведет TRUE для примера с переменной value01 и FALSE для примера с переменной value02:
$value01 = '192.168.0.1'; if(filter_var($value01,FILTER_VALIDATE_IP)) { echo 'TRUE'; } else { echo 'FALSE'; } echo '<br /><br />' $value02 = '1.2.3.4.5.6.7.8.9'; if(filter_var($value02,FILTER_VALIDATE_IP)) { echo 'TRUE'; } else { echo 'FALSE'; }
FILTER_VALIDATE_URL: Проверяет, является ли переданное значение корректным URL-адресом. Она не проверяет, она не проверяет доступность ресурса, а лишь то, что соблюдена структура URL-адреса. Скрипт ниже выведет TRUE для примера с переменной value01 и FALSE для примера с переменной value02:
$value01 = 'http://net.tutsplus.com'; if(filter_var($value01,FILTER_VALIDATE_URL)) { echo 'TRUE'; } else { echo 'FALSE'; } echo '<br /><br />' $value02 = 'nettuts'; if(filter_var($value02,FILTER_VALIDATE_URL)) { echo 'TRUE'; } else { echo 'FALSE'; }
FILTER_SANITIZE_STRING: По умолчанию данный фильтр удаляет любую некорректную или не разрешенную информацию в строке. Например, она удалит любые тэги HTML вроде <strong> или <script> из входящей строки:
$value = '<script>alert('TROUBLE HERE');</script>'; echo filter_var($value, FILTER_SANITIZE_STRING);
Данный скрипт удалит тэги и вернет следующее:
alert('TROUBLE HERE');
FILTER_SANITIZE_ENCODED: Многие программисты используют функцию urlencode(). Данный фильтр по сути выполняет те же функции. Например, следующий пример выполнит кодирование любых спецсимволов и пробелов во входящей строке:
$value = '<script>alert('TROUBLE HERE');</script>'; echo filter_var($value, FILTER_SANITIZE_ENCODED);
Скрипт закодирует пунктуацию, пробелы, скобки и вернет следующее:
%3Cscript%3Ealert%28%27TROUBLE%20HERE%27%29%3B%3C%2Fscript%3E
FILTER_SANITIZE_SPECIAL_CHARS: Данный фильтр по умолчанию производит HTML-кодирование спецсимволов вроде кавычек, амперсандов и скобок. Так как демо-страница не может явно показать это (так как HTML-кодированные спецсимволы будут проинтерпретированы браузером и отображены), вы можете увидеть это, если заглянете в исходный код:
$value = '<script>alert('TROUBLE HERE');</script>'; echo filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
Произойдет конвертация спецсимволов в их HTML-сущности:
<script>alert('TROUBLE HERE');</script>
FILTER_SANITIZE_EMAIL: Этот фильтр делает именно то, о чем подумал каждый из нес. Он удаляет из адреса символы, которых не должно быть в адресе (круглые и квадратные скобки, двоеточия и т.п.) Положим, вы случайно добавили в ваш адрес скобки вокруг какой-либо буквы (не спрашивайте, как, включите ваше воображение:))
$value = 't(e)st@example.com'; echo filter_var($value, FILTER_SANITIZE_EMAIL);
Произойдет удаление скобок и вы получите на выходе свой чистый и красивый е-mail:
test@example.com
Это отличный фильтр, который можно использовать в формах для ввода e-mail, особенно в паре с FILTER_VALIDATE_EMAIL, что позволит снизить число ошибок пользователей и предотвратить атаки типа XSS.
FILTER_SANITIZE_URL: Данный фильтр похож на предыдущий. Он удаляет любые символы, недопустимые в URL. К примеру, скажем, в адресе случайно оказался знак "®". Опять же, как он туда попал - сплошная загадка.
$value = 'http://net.tuts®plus.com'; echo filter_var($value, FILTER_SANITIZE_URL);
Таким образом мы удалим ненужный знак "®" и получим нормальный адрес:
http://net.tutsplus.com
FILTER_SANITIZE_NUMBER_INT: Данный фильтр похож на FILTER_VALIDATE_INT, но вместо простой проверки на то, является ли число целым, он еще и удаляет все, что не является целым числом. Отличная вещь, особенно против надоедливых спам-ботов и обманщиков, норовящих ввести в поле какую-нибудь ерунду:
$value01 = '123abc456def'; echo filter_var($value01, FILTER_SANITIZE_NUMBER_INT); echo '<br />'; $value02 = '1.2.3.4.5.6.7.8.9'; echo filter_var($value02, FILTER_SANITIZE_NUMBER_INT);
Оба набора символов преобразуются и на выходе получаем следующую картину:
123456 123456789
FILTER_SANITIZE_NUMBER_FLOAT: Похож на FILTER_VALIDATE_INT. Точно также позволяет добиться аналогичного эффекта:
$value01 = '123abc456def'; echo filter_var($value01, FILTER_SANITIZE_NUMBER_FLOAT); echo '<br />'; $value02 = '1.2.3.4.5.6.7.8.9'; echo filter_var($value02, FILTER_SANITIZE_NUMBER_FLOAT);
Оба набора символов преобразуются и на выходе получаем следующую картину:
123456 123456789
Как же нам быть, если нужно сохранить десятичное число - спросите вы, например:
$value = '1.23'; echo filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT);
Точка будет удалена и возвращено значение:
123
Одна из главных причин того, что фильтры FILTER_SANITIZE_NUMBER_FLOAT и FILTER_SANITIZE_INT разделены - это возможность использовать специальный флаг FILTER_FLAG_ALLOW_FRACTION, который идет как третий параметр, передаваемый функции filter_var:
$value = '1.23'; echo filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
Так мы можем сохранить десятичную часть и получить в итоге:
1.23
Опции, флаги и контролирующие массивы - Майн Готт!
Флаг, использовавшийся в предыдущем примере - лишь один из способов получить более детальный контроль над типами данных, которые будут подвергаться очистке, определениями ограничителей, тем, как обрабатываются фильтрами массивы и др. Узнать больше про флаги и функции, используемые всвязи с применением фильтров вы можете в руководстве PHP, в части, посвященной Фильтрам - php.net/manual/en/book.filter.php.
Другие методы очистки информации средствами PHP
Сейчас мы рассмотрим несколько ключевых методов очистки данных для предотвращения атаки на ваше приложение. Они особенно актуальны для тех приложений, которые по-прежнему работают на PHP4, так как появились с его релизом.
htmlspecialchars: эта функция PHP преобразует 5 специальных символов в соответствующие HTML-сущности.
Преобразованию подвергаются:
& (амперсанд)
" (двойные кавычки) когда не установлен флаг ENT_NOQUOTES
’ (одинарные кавычки) только когда установлен флаг ENT_QUOTES
< (меньше, чем)
> (больше, чем)
Используется данная функция точно так же, как и любая другая в PHP:
echo htmlspecialchars('$string');
htmlentities: Подобно функции htmlspecialchars эта функция конвертирует специальные символы в их HTML-сущности. Все отличие в том, что в данном случае конвертируются все спецсимволы, которые могут быть конвертированы. Это достаточно распространенный метод для запутывания (обфускации) e-mail-адресов от спам-ботов, так как далеко не все из них настроены на чтение html-сущностей:
echo htmlentities('$string');
mysql_real_escape_string: Это функция MySQL, помогающая защититься от атак типа SQL-инъекция. Считается хорошей практикой (а по сути необходимостью) пропускать всю информацию передаваемую SQL-запросу через эту функцию. Она экранирует все опасные спецсимволы, которые могут вызвать проблемы и стать причиной того, что little Bobby Tables уничтожит еще одну таблицу в школьной базе данных.
$query = 'SELECT * FROM table WHERE value='.mysql_real_escape_string('$string').' LIMIT 1,1'; $runQuery = mysql_query($query);
Пользовательские функции
Для многих людей встроенных функций и фильтров может оказаться недостаточно. Часто может потребоваться более жесткая и узкая валидация или очистка. Чтобы добиться нужных результатов многие сами пишут функции для валидации данных. В качестве примера можно привести вариант поиска в базе данных значений определенного типа, вроде:
function checkZipCode($value) { $zipcheck = 'SELECT COUNT(*) FROM `database`.`zipcodes` WHERE value="'.filter_var(mysql_real_escape_string($value),FILTER_SANITIZE_NUMBER_INT).'"'; $count = mysql_query($zipcheck); if($count==1) { return TRUE; } else { return FALSE; } }
Другие пользовательские функции могут быть не связаны с базой напрямую, а заниматься подготовкой информации перед вставкой в базу:
function cleanString($string) { $detagged = strip_tags($string); if(get_magic_quotes_gpc()) { $stripped = stripslashes($detagged); $escaped = mysql_real_escape_string($stripped); } else { $escaped = mysql_real_escape_string($detagged); } return $escaped; }
Возможности почти безграничны, особенно при использовании регулярных выражений. Однако, для большинства случаев, применение фильтров уже способно решить необходимые задачи.
Michael Owens
По материалам www.net.tutsplus.com
Перевод - Дмитрий
Исходники
P.S. Если вы хотите взять хороший старт в изучении PHP, обратите внимание на бесплатный курс по созданию своей CMS-системы на PHP с нуля. Используется объектно-ориентированный подход, поэтому вы сразу будете привыкать к хорошему:
Понравился материал и хотите отблагодарить?
Просто поделитесь с друзьями и коллегами!
Смотрите также: