Лучший способ предотвратить SQL-инъекцию при подготовке оператора невозможен
Мне приходится работать на старом сайте с серьезными проблемами безопасности: SQL-инъекции действительно легко выполнить.
Очевидно, что лучший способ предотвратить такого рода атаки - избежать того, что используется в запросе (подготовить операторы с помощью PDO, mysql_real_escape_string с MySql и т. Д.), Но мы не можем сделать это быстро: весь сайт является процедурным PHP (без класса), запросы "готовятся" повсеместно, ежедневно появляются сотни страниц и тысячи пользователей, и новая версия появится как можно скорее.
Таким образом, с этого утра при каждом запросе вызывается следующая функция для обнаружения подозрительных параметров POST или GET на основе ключевых слов.
const SQLI_UNSAFE = 3;
const SQLI_WARNING = 2;
const SQLI_SAFE = 1;
const SQLI_MAIL_DEST = 'monmail@mondest.com';
function sqlicheck() {
$params = array_merge($_GET, $_POST);
$is_warning = false;
foreach($params as $key=>$param) {
switch(getSafeLevel($param)) {
case SQLI_SAFE:
break;
case SQLI_WARNING:
$is_warning = true;
break;
case SQLI_UNSAFE:
mail(SQLI_MAIL_DEST, 'SQL INJECTION ATTACK', print_r($_REQUEST, true).' '.print_r($_SERVER, true));
header('Location: http://monsite/404.php');
exit();
}
}
if($is_warning === true) {
mail(SQLI_MAIL_DEST, 'SQL INJECTION WARNING', print_r($_REQUEST, true).print_r($_SERVER, true));
}
}
function getSafeLevel($param) {
$error_words = array('select%20','drop%20','delete%20','truncate%20','insert%20','%20tbclient','select ','drop ','delete ','truncate ','insert ',);
$warning_words = array('%20','select','drop','delete','truncate', ';','union');
foreach($error_words as $error_word) {
if(stripos($param, $error_word) !== false) return SQLI_UNSAFE;
}
foreach($warning_words as $warning_word) {
if(stripos($param, $warning_word) !== false) return SQLI_WARNING;
}
return SQLI_SAFE;
}
Кажется, что это обнаруживает некоторые виды атак, но это явно очень просто. Есть идеи по улучшению? Любая серьезная проблема?
2 ответа
Во-первых, убедитесь, что пользователь базы данных, выполняющий запросы, имеет только права выбора, обновления, удаления. Если пользователь не может выполнить удаление, это никак не произойдет (это предполагает, что вашим пользователям никогда не потребуется создавать или удалять таблицы, но если они это сделают, вы можете создать разрешения на уровне таблиц для защиты больших таблиц).
Во-вторых, ваш сценарий скажет вам только то, что люди используют; он не будет делать проверку всего сайта о том, какие запросы возможны; если есть раздел вашего сайта, который не используется часто, вы не получите никакой почты, сообщающей вам. Лучше просто прочесать код с помощью инструмента поиска.
После этого вы должны начать изменять код и выполнять экранирование и проверку, и это займет некоторое время.
Почему бы просто не использовать real_escape_string()
htmlentities()
удаляет и фильтрует классы php и регистрирует SQL-запросы, чтобы вы могли видеть, что люди отправляют вам, используйте sha256 с некоторым md5, все будет в порядке, иначе для быстрого действия просто отправьте данные для входа в двоичном формате