Как стать автором
Обновить

Безопасность PHP — Проверка поступивших данных

Постановка задачи

Одним из главных правил php-программиста является необходимость проверки поступающих от пользователя данных. Существует множество способов организовать данную проверку, но, к сожалению, не все они безопасны и информативны, поэтому Я решил поделиться своим опытом.
Сразу обозначу основные моменты:
  • register_globals off
  • magic_quotes_gpc off

Ну, по поводу register_globals Я ничего объяснять не собираюсь, так как читающий эту статью точно уже знает о этой проблеме. Повторять общеизвестные факты Я не буду.
Отключенные magic_quotes_gpc объясняются тем, что Я ориентируюсь на использование технологии ajax для своих проектов и «магические кавычки» — это «большая палка в колёса». По мне лучше экранировать необходимые данные вручную, чем потом искать среди ответов сервера на ajax-запрос ошибочное двойное экранирование.

Функция логирования

Для того чтобы проверка данных была не только обеспечивала безопасность, но и была информативна, т.е. позволяла выявить атаки на ваш проект, необходимо использовать логирование. Ниже привожу свою собственную функцию логирования, которая нам понадобиться далее.

//------------------------ Процедура записи в лог фаил -----------------------//
function writelog($typelog, $log_text) {
$log = fopen('logs/'.$typelog.'.txt','a+');
fwrite($log, "$log_text\r\n");
fclose($log);
}
//----------------------------------------------------------------------------//

Здесь особо пояснять нечего — элементарная запись текстовой строчки $log_text в фаил лога $typelog (см. работа с файлами в PHP).

Функция проверки поступивших данных

Все поступившие данные необходимо в начале привести к подобающему виду, затем проверить на «плохие» слова и произвести экранирование кавычек. Давайте, всё разберем на конкретном примере:

//--------------------- Процедура обработки получаемых данных ----------------//
function checkrequest($textrequest) {
// Удираем лишние пробелы
$textrequest = trim($textrequest);
// Производим конвертацию в необходимую кодировку (нужно если у Вас проект использует кодировку отличную от UTF-8)
// $textrequest = iconv("UTF-8", "WINDOWS-1251", $textrequest);
// Фиксируем атаки
if (preg_match("/script|http|<|>|<|>|SELECT|UNION|UPDATE|exe|exec|INSERT|tmp/i",$textrequest)) {
writelog('hack', date("y.m.d H:m:s")."\t".$_SERVER['REMOTE_ADDR']."\t".$textrequest);
$textrequest = '';
}
// Очищаем опасные запросы
if (preg_match("/[^(\w)|(А-Яа-я-&=.,@():;)|(\s)]/",$textrequest)) {
$textrequest = '';
}
// Если не число, то экранируем ковычки
if (!is_numeric($textrequest)) {
$textrequest = mysql_real_escape_string($textrequest);
}

return $textrequest;
}
//----------------------------------------------------------------------------//


Ну, а теперь всё по порядку:
  • Первой строкой кода удаляем лишние пробелы — они нам не нужны
  • Второй строкой кода производим конвертацию в необходимую кодировку. Это нам понадобиться если мы используем в проекте кодировку отличную от UTF-8 и работаем с ajax. Зачем нам это нужно? На своём личном опыте Я убедился, что несмотря на то, что везде где можно (установки сервера, базы, заголовки) указывается используемая кодировка, броузеры могут реагировать совершенно непредсказуемо и присылать запросы в кодировке UTF-8. Естественно, если Вы используете кодировку UTF-8, то данная строка не нужна, поэтому она у меня закомментирована. Хочу предупредить, что и эта строка не «панацея», так как конвертация не работает для сложных структур (например, массивов).
  • Третьей строкой кода фиксируем «нехорошие» слова в поступивших данных. Если такое слово найдено, делаем запись в лог (см. функцию выше) и очищаем переменную поступивших данных.
  • Четвёртой строкой кода проверяем поступившие данные по «белому списку». «Белый список» — это список допустимых символов, «черный список» — запрещённых символов, соответственно. Почему проверяем именно по первому списку? Потому что, иначе имеется возможность забыть указать какой-нибудь нехороший символ, т.к. их весьма-весьма много (примеры приводить не буду — об этом уже много написано до меня). В моём случаи Я разрешаю использовать только латинские и русские буквы, цифры и несколько необходимых символов. Если данные не прошли проверку, то мы их очищаем.
  • Пятой строкой кода экранируем кавычки при помощи mysql_real_escape_string. Обратите внимание, что экранируются только не числовые значения (!is_numeric). Это делается для того, чтобы не использовать лишний раз функцию mysql_real_escape_string, которая «съедает» примерно 0.0001 сек.

Как видите ничего сложного, но весьма эффективно. В среднем для проектов с кодировкой UTF-8 (т.е. без конвертации данных) скорость выполнения примерно 0.0003-0.0004 сек.
Применяется функция стандартно:
$id = checkrequest($_POST['id']);
Хочу обратить внимание, что Я просто игнорирую неверные данные поступившие от пользователя и их очищаю: $textrequest = '';. Соответственно, основной код написан из расчета, что некоторые данные могут быть пустыми. Это несколько неудобно, зато очень безопасно.
Если же у вас некоторые поступившие данные могут не пройти проверку, то к ним можно всегда обратиться напрямую: $id = $_POST['id'];
Надеюсь, данная статья направить на путь истинный многих начинающий php-программистов и поможет улучшить свои функции у профессионалов.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.