PHP: фильтр от XSS и SQL-инъекций.
08 мая 2008Предлагаю небольшое исследование на тему безопасности веб-приложений и фильтрации вводимых данных. Скорее всего, я ещё вернусь к этой теме, но сейчас больше хочется не писать трехтомник, а сохранить и упорядочить собранное и проверенное. В частности, речь пойдёт о фильтрационной защите от XSS-атак и SQL-инъекций.
В PHP есть ряд специальных функций для работы со строками, применимых для этой цели. Вот они:
strip_tags() — вырезает теги HTML и PHP из строки.
htmlspecialchars() — конвертирует только специальные символы (‘&’, ‘»‘, »’, ‘<‘ и ‘>’) в HTML сущности (‘&’, ‘"’…). Используется для фильтрации вводимых пользователем данных для защиты от XSS-атак.
htmlentities() — конвертирует все символы в строке (кроме букв) в мнемоники HTML. Используется для защиты от XSS, являясь более гибким аналогом htmlspecialchars.
stripslashes() — удаляет заэкранированные символы (после преобразования в сущности предыдущими функциями их незачем экранировать). Обычно используется в связке с проверочной функцией get_magic_quotes_gpc(), показывающей текущую установку конфигурации magic_quotes_gpc.
Эта конфигурация влияет на то, как будут обрабатываться специальные символы, содержащиеся в данных, передаваемых пользователем (массивы $_GET, $_POST, $_COOKIE). При magic_quotes_gpc = 1 эти спецсимволы (одиночные и двойные кавычки, обратный слэш, байт NULL) автоматически экранируются. При magic_quotes_gpc = 0 все данные передаются в таком виде, в каком их ввел пользователь. В последнем случае в целях безопасности требуется обрабатывать передаваемые данные.
mysql_real_escape_string — мнемонизирует специальные символы в строке для использования в операторе SQL с учётом текущего набора символов в кодировке соединения. Иными словами, функция превращает любую строку в правильную и безопасную для MySQL-запроса. Используется для очистки всех данных, передающихся в MySQL-запрос для защиты от SQL-инъекций.
На основе этих данных, можно собрать функцию для однозначной очистки непроверенных данных. Эта функция получает две переменные — строку с введёнными символами и булево значение для определения — требуется ли приведение к MySQL-запросу. Возвращает функция очищенную строку. Конечно, это не панацея, но функцию легко изменить под собственные нужды.
InputClear.phps - фильтрация введённых пользователями данных.
Если кто-то обнаружит ошибки, или знает способ лучше — добро пожаловать в комментарии, мне очень интересно.
В ru_webdev эту тему уже сто раз поднимали. В итоге пришли к тому, что любую настройку PHP об экранировании данных нужно сначала отменить, а дальше на основе требований к формату данных — их экранировать.
Например, перед обработкой данных сделать:
if ( get_magic_quotes_gpc() )
while ( list($k,$v)=each($_POST) $_POST[$k]=stripslashes($v);
А потом уже в зависимости от требований приводить к необходимому формату. Например, для цифр сделать $var = intval($var).
Вот способ надёжней: http://htmlpurifier.org/
Лёхха, спасибо, довольно чётко подвёл черту. Я что-то давно в ru_webdev не заглядывал…
Sam, интересная штука, похоже достойное решение, хотя поглубже ещё не посмотрел. Как он по гибкости \ скорости?
Посмотрите это решение: http://dklab.ru/lib/DbSimple/ (плейсхолдеры для защиты от SQL-инъекций. 100% работает. Проверено на десятке работающих проектов.
mysql_real_escape_string использует апи
вот, то что надо
protected function quote($value)
{
if (is_int($value) || is_float($value)) {
return $value;
}
return «‘» . addcslashes($value, «00\n\r\\’\»32») . «‘»;
}
честно говоря в рамках серьезных фреймворков это тема ИМХО не актуальна. там данные как правило в большинстве случаев без вмешательства разработчика приводятся к безопасному виду(по крайней мере в случае sql запросов), с xss канеш сложнее…
По скорости не очень. По надёжности — супер.
goDB — библиотека для работы с MySQL из PHP http://pyha.ru/go/godb/
Интерфейс над MySQLi, сам проводил бенчмарки, по скорости сравнимо с MySQLi (т.е. во многом быстрее классического MySQL API для PHP), а иногда по скорости даже превышает. Но никак не медленнее! Юзаю как в своих личных проектах, так и довольно крупных проектах с большим количеством запросов.
adw0rd, мы не про кулибинские надстройки для работы с базами данных, мы про фильтры строк для защиты от XSS.
Влад, ты зарезал часть теста с ссылками про XSS…
А про либу для работы с СУБД MySQL я привел из-за поста Ярослава Витязева. В goDB, для корректной вставки данных в запрос — тоже используется механизм плейсхолдеров.
Просто таких ссылок можно миллион накидать — и на ru_webdev, и на php.ru, и ещё куда угодно. Лучше про плейсхолдеры расскажи :-)
Гы. Вы только что наблюдали живописное падение сервака =) После выходных буду переезжать на большой и красивый новый сервак, на этом тесно стало уже.
[…] Безопастность прежде всего, читаем […]
Способ ничего так… Но есть получше. А в статье можно почерпнуть многое. все собрано — информация которую ищешь. спс.
[…] о фильтрационной защите от XSS-атак и SQL-инъекций. Блог интернет-разработчика — PHP: фильтр от XSS и SQL-инъекц
После того как наконец то яндекс зафильтровал xss статья уже не так актуальна..но про sql-иньекции взял на заметку)
Kenno, а при чём тут Яндекс? Нужно же смотреть немного глубже. Пару лет назад я попал в газеты, вытащив с помощью XSS куки администратора одного крупного ресурса и устроив там настоящий апокалипсис…
ну да)
cheatsheet почти в тему
я бы посоветовал еще с помощью str_replace пресекать служебные слова, подобные select, delete и другие.
свое видение безопасности описал здесь:
http://webamator.ru/news.php?extend.23.5
Логично ;-)
>> я бы посоветовал еще с помощью str_replace пресекать служебные слова, подобные select, delete и другие
ага, а потом появляются «супер» продукты в которых невозможно написать (в комментарии/посте допустим) какое-то слово или символ… скажу как пользователь некоторых таких продуктов: бесит!:)
1) данные должны храниться (в БД, в переменной) в чистом виде (если пришли экранированные, сразу сняли экранирование).
2) перед тем как использовать защититься соотв. образом:
a) для вывода в html/xml: htmlspecialchars() и т.п.
b) для вставки в sql: специализированной функцией экранирования для конткретной СУБД (mysql_real_escape_string(), pg_escape_string() и т.п., здесь нюанс если будет не верно настроены кодировки на субд/пхп то возможно экранирования не будет сделано, поэтому за этим тоже нужно следить).
с) ещё куда-то? обычно всегда уже есть готовая функция для экранирования.
Все.
Серж, браво. Пожалуй, надо переписывать мою функцию с учётом сказанного, ибо не с чем тут спорить.
Отмажусь тем, что функция составлена в качестве иллюстрации принципов и конечно требует заточки под собственные нужды перед употреблением.
Провероку!
magic_quotes_gpc добавляет слэши лишь к запросам $_GET, $_POST, $_COOKIE, $_REQUEST. По этому и называется magic_quotes_GPC. Следовательно при запросах типа $_SERVER, $_ENV, $_FILES, $_SESSION кавычки не слэшируются. А если учесть, что такие запросы, как $_SERVER (приходящие со стороны клиента) можно подделать, например прописав в HTTP_REFERER или HTTP_USER_AGENT на свой запрос, можно считать, что уязвимость существует. (в случае если скрипт получает эти данные и записывает в бд например)
if(get_magic_quotes_gpc ()) {
$input = stripslashes ($input);
// убрали лишнее теперь экранирование.
}
Кусок кода из твоей функции. Получается, что если get_magic_quotes_gpc() вернет true, экранирование произойдет, а если false, соответственно, нет.
http://secureblog.org/pub/bezopasny_cod_chast_1.html
здесь я описал эту и некоторые другие недоработки при кодинге на php.
2c0nst:
По вашей статье (раз уж здесь отписались, здесь и откоменчу):
1) >> Уязвимости в функциях ereg(), ereg_replace(), mb_ereg_match()
Это не уязвимости, как я и писал нужно защищаться от данных пришедших от клиента… Есть такая функция preg_quote() которая экранирует спец. символы для использования в регулярных выражениях. Незнаю как в сочетании с ereg_* функциями она работает, но с preg_* точно работает превосходно.
2) >> Функция intval() возвращает TRUE, если в аргументе присутствует хотя бы одна цифра на первом месте.
Вы что-то перепутали… Смотрим ман:
int intval ( mixed $var [, int $base ] )
Нет тут никаких — true.
И грамотные программисты используют не так как вы показали (в условии), а так: $sql = «SELECT * FROM table1 WHERE id=».intval($id);
3) про magic_quotes_gpc
Повторюсь… Лучше вообще это не юзать. Все данные храним и получаем в исходном виде (не экранированном), а перед тем как заюзать уже защищаемся.
Кстати опять же смотрим ман:
magic_quotes_gpc —
«This feature has been DEPRECATED and REMOVED as of PHP 6.0.0»
Вот ман ещё даж не прошерстят, а уже других учат… Ужос:)
2serge
1) В заметке написан лишь способ как можно защитить свое приложение. Как это реализовывать программист это другой вопрос.
2) Здесь я действительно ошибся, имелась ввиду не функция intval(), а if(intval($id))..
> И грамотные программисты используют не так как вы показали (в условии), а так: $sql = “SELECT * FROM table1 WHERE id=”.intval($id);
в том то и дело, что то выражение которое я указал, неправильное (показал на примере). читайте внимательнее.
$var = intval($_GET[‘var’]); // $var = (int)$_GET[‘var’];
mysql_query(«SELECT $var FROM table»);
…
^^^ а здесь указал один из правильных вариантов.
3) разве я призываю юзать magic_quotes_gpc? покажите мне строку где вы это вычитали. Я говорю о том, что многие используют такие конструкции, в которых не фильтруются входящие данные через $_SERVER, etc… Но ничуть не призываю его использовать.
Чего-то не понял в практическом применении сей функции.
Не смотря на то что «функция составлена в качестве иллюстрации принципов», кажется нелогичным такая последовательность действий:
// мнемонизировали строку.
и только после:
//режем теги.
Собственно после того как мнемонизировали все теги, то что резать-то?
Может не я в чем-то не прав?
Долго думал… Да, похоже косяк, хотя функция проверку прошла. Надо пробовать ещё раз и переписывать.
Спасибо :-)
Нет-таки точно я чего-то не понял…
Например параметр $input, пусть будет у нас таким: «\n»
После прохода этой замены:
$input=str_replace («\n»,» «, $input);
Он успешно выводиться в первозданном виде «\n» (без кавычек разумеется).
На сколько я понимаю параметром для str_replace в данном случае должен быть «\\n», что бы все корректно искалось-заменялось.
Вот и вопрос, не очень скромный: функция тестилась реально? :)
(Если не прав, прошу прощения…)
С уважением, сами-видите-кто :)
При юзаньи слов «Антивирусная Поддержка» выкидывает «y поддержка»… :) И еще много разных русских слов в коды превращает
Виноват. Забыл сменить кодировку в функции
Функция реально тестилась и вполне успешно, хотя небольшой косяк там, кажется, есть.
Слова она не режет точно — копать или в сторону настроек PHP или кодировок текста.
на мой взгляд более удобно подключить для обеспечения безопасности htacсess где явно запретить все кроме букв, цифр, _, -.
как пример
RewriteRule ^([-a-zA-Z_0-9\/]*)$ index.php?url=$1 [QSA,L]
а уже не посредственно при обработке $url в скрипте включить обрабтку на корректность запроса
при таком варианте мы избегаем XSS (ИМХО), далее включаем проверку на наличие запрашиваемых данных в базе, и вслучае соотвествия выводим информацию, в случае несоотвествия 404
прошу прощения если немного сумбурно выразил мысль
От XSS может спасти следующий набор RegExp =)
$this->unsafe_html[] = ‘!javascript\s*:!is’;
$this->unsafe_html[] = ‘!vbscri?pt\s*:!is’;
$this->unsafe_html[] = ‘!unsafe_html[] = ‘!]*[^a-z]onabort\s*=!is’;
$this->unsafe_html[] = ‘!]*[^a-z]onblur\s*=!is’;
$this->unsafe_html[] = ‘!]*[^a-z]onchange\s*=!is’;
$this->unsafe_html[] = ‘!]*[^a-z]onfocus\s*=!is’;
$this->unsafe_html[] = ‘!]*[^a-z]onmouseout\s*=!is’;
$this->unsafe_html[] = ‘!]*[^a-z]onmouseover\s*=!is’;
$this->unsafe_html[] = ‘!]*[^a-z]onload\s*=!is’;
$this->unsafe_html[] = ‘!]*[^a-z]onreset\s*=!is’;
$this->unsafe_html[] = ‘!]*[^a-z]onselect\s*=!is’;
$this->unsafe_html[] = ‘!]*[^a-z]onsubmit\s*=!is’;
$this->unsafe_html[] = ‘!]*[^a-z]onunload\s*=!is’;
$this->unsafe_html[] = ‘!]*[^a-z]onerror\s*=!is’;
$this->unsafe_html[] = ‘!]*[^a-z]onclick\s*=!is’;
Если лень думать — есть специальная либо Sanitize в PEAR. Благо, почти везде стоит :)
serge .
Написано 18 мая 2008 года в 20:28
3) про magic_quotes_gpc
Повторюсь… Лучше вообще это не юзать. Все данные храним и получаем в исходном виде (не экранированном), а перед тем как заюзать уже защищаемся.
Кстати опять же смотрим ман:
magic_quotes_gpc —
“This feature has been DEPRECATED and REMOVED as of PHP 6.0.0″
———
отключать надо, конечно
php_flag magic_quotes_gpc 0
php_flag magic_quotes_runtime 0
———
но и экранировать данные ,перед занесением в базу, ручками тоже необходимо.
foma .
Написано 7 июня 2008 года в 12:27
на мой взгляд более удобно подключить для обеспечения безопасности htacсess где явно запретить все кроме букв, цифр, _, -.
————
вариант, но иногда бывают обстоятельства когда юзеру нужно дать возможность,(например написать php или javascript код)
htmlentities($str, ENT_QUOTES); нормаль,но не дает расслабляться.
Спасибо, очень пригодилась Ваша функция =)
А зачем убирать переносы строк, может я тупой? Объясните пожалуйста.
Вот смотрите дан сайт :
Морда написана на ActionScript (Flash)
Далее идёт связь с php путём xml
Php в свою очередь связан с MySql.
Flash кодирует любую введеную пользователем информацию в base64 и передаёт php, тот в свою очередь записывает в MySql.
Задумка проста — кодированная информация ничего не ломает, а скомпилированный ролик чихал на все тэги, скобки и т.д.
ACTIONSCRIPT 2.0: BASE64 ENCODER
Проблема решена или я что-то упустил ? :)
Flash — зло, он скоро умрёт.
Flash это один из основных продуктов Adobe Systems, компании с многомиллиардной (!) прибылью.
Компании 10-ки продуктов которой покупают 1000 организаций по 2000$ (в среднем) ежегодно, и они вряд ли просто так растануться с Flash. Следовательно потребители (юзеры) в любом случае будут вынуждены использовать Flash для посещения сайтов этих 1000 организаций.
Говорю как человек с экономическим образованием:
Может Flash и зло, но процветать он будет и тогда, когда мы с вами посинеем и зачахнем.
Я сейчас этот текст пишу с устройства одной компании с ещё более интересными оборотами, которое не поддерживает ни флеш, ни его мертворожденный дубль сильверлайт. Это устройство на данный момент самый популярный гаджет в мире, если что.
А вебмастеров, чьи сайты не работают без глючной проприетарной надстройки, надо увольнять. Или силой заставлять учить canvas, svg, …
Угу.
Мое предложение помогло от xss? А то я, как бы, сам то этот способ не пробовал, интересно.
Нет, твое предложение лишило возможности пользоваться сервисом более 10% пользователей, включая меня :)
А хотите 100% защиту от таких атак в любом браузере?
ссылка.
Скрипты надо бы проверить, дабы точно сказать — насколько эти 100% близки к истине. Но причём тут вообще браузер? :)
А Вы не слышали про JavaScript в исходном коде изображений, и связанную с этим проблему безопасности в IE вплоть до версии 8?