Я уже не раз излагал свои мысли по поводу принципов грамотной фильтрации входящих данных, этот пост будет своеобразным дополнением, подкрепленным живым примером.
Недавно столкнулся с интересной 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