PHP → Дыры в большинстве preg_match фильтров
Мне часто приходится разбираться в чужих скриптах. Много этих скриптов содержит регулярные выражения, и я заметил, что большинтсво регулярных выражений в этих скриптах написаны с ошибками.
Большинство PHP-разработчиков используют символы ^ и $ в своих регулярных выражениях, до конца не понимая что они получают в итоге. Можно встретить очень много фильтров наподобие такого:
<?php
$clean = array();
if (preg_match("/^[0-9]+:[X-Z]+$/", $_GET['var'])) {
$clean['var'] = $_GET['var'];
}
?>
Довольно распространенный способ фильтрации входных данных, не так ли?
Однако, проблема в том, что автор такого регулярного выражения читал документацию не совем внимательно и ошибочно полагает,
что символ доллара ($) однозначно определяет конец строки. На самом деле это не совсем так. Даже в документации по PHP сказано,
что символ $ означает конец строки или "почти конец", имея ввиду что за ним еще может следовать один
единственный символ переноса строки (\n). А это означает, что следующий запрос успешно пройдет через этот фильтр:
http://server.tld/index.php?var=012345:XYZ%0a
В некоторых случаях символ переноса строки может быть опасным. Например, когда вы хотите предотвратить разделение ответов HTTP
или Email-инъекции. Чтобы это исправить, необходимо добавить в регулярное выражение
модификатор D. Этот модификатор указывает, что символ $ действительно является концом текста и ничем более. Вот
правильный код:
<?php
$clean = array();
if (preg_match("/^[0-9]+:[X-Z]+$/D", $_GET['var'])) {
$clean['var'] = $_GET['var'];
}
?>
Я надеюсь, что эта статья помогла вам и отныне вы будете писать только правильные фильтры с использованием регулярных выражений.
