Я уже не раз излагал свои мысли по поводу принципов грамотной фильтрации входящих данных, этот пост будет своеобразным дополнением, подкрепленным живым примером.
Недавно столкнулся с интересной SQL-инъекцией на одном популярном ресурсе, посвященному взлому и хакингу. С одной стороны, признаки успешно внедренного SQL-кода были явными, однако поведение уязвимого веб-приложения при особых запросах говорило о наличии некой фильтрации на определенные ключевые слова. Например запрос ‘ OR 1=1/* успешно проходил, но, подставив AND вместо OR, я постоянно получал пустую страницу – это, очевидно, следствие возникшей ошибки синтаксиса SQL-запроса. Проанализировав ответы скрипта, я убедился, что, во-первых, проверка на ключевые слова действительно имела место, и, во-вторых, производилась не просто проверка, а вырезание из строки запроса всех совпадений со списком ключевых слов. На PHP это выглядело бы примерно так:
<?php
$badwords = array("AND","UNION","SELECT","WHERE","INSERT","UPDATE","DELETE");
str_replace($badwords,"",$GET['id']);
?>
Обойти такую проверку сущий пустяк.
Все очень просто: нужно всего лишь вставить в фильтрумое слово еще какое-нибудь слово из списка. Таким образом, будут однократно вырезаны опасные слова, что сделает инжектируемые данные приемлемыми для синтаксиса конечного SQL-запроса. Пример:
-1 UNIandON SELEandCT username,password FRandOM users WHandERE id=1/*
В чем же заключается ошибка подобной фильтрации? Все дело в том, что PHP-сценарий должен производить лишь проверку со списком опасных слов, но не обработку путем вырезания. Пример грамотной реализации можно увидеть в Danneo CMS (/base/danneo.track.php):
<?php
$baddata = array("UNION",
"OUTFILE",
"FROM",
"SELECT",
"WHERE",
"SHUTDOWN",
"UPDATE",
"DELETE",
"CHANGE",
"MODIFY",
"RENAME",
"RELOAD",
"ALTER",
"GRANT",
"DROP",
"INSERT",
"CONCAT",
"cmd",
"exec",
"--",
// HTML LINE
"\([^>]*\"?[^)]*\)",
"<[^>]*body*\"?[^>]*>",
"<[^>]*script*\"?[^>]*>",
"<[^>]*object*\"?[^>]*>",
"<[^>]*iframe*\"?[^>]*>",
"<[^>]*img*\"?[^>]*>",
"<[^>]*frame*\"?[^>]*>",
"<[^>]*applet*\"?[^>]*>",
"<[^>]*meta*\"?[^>]*>",
"<[^>]*style*\"?[^>]*>",
"<[^>]*form*\"?[^>]*>",
"<[^>]*div*\"?[^>]*>");
foreach($baddata as $badkey => $badvalue){
if(is_string($inputdata) && eregi($badvalue,$inputdata)){ $badcount=1; }
}
/* ... */
if($badcount==1){
/* ... */
}
?>
Что касается того уязвимого сайта хакеров, то я его оставил в покое, тем более что он был грузинским, а войны я не хочу =)

Leave a Reply