SQL инъекции и какие есть заблуждения в устранении её
Сегодня хочу затронуть тему SQL-инъекций и особенно остановиться на том, как их правильно устранять.
━━━━━━━━━━ Что такое SQL-инъекция ━━━━━━━━━━
SQL-инъекция — это уязвимость, при которой пользовательские данные попадают в SQL-запрос так, что атакующий может изменить его логику.
Условный пример: • SELECT * FROM users WHERE username = '$username' AND password = '$password';
Если в username передать: • ' OR 1=1; --
то итоговая логика запроса может измениться: • SELECT * FROM users WHERE username = '' OR 1=1; --' AND password = '...'
Здесь OR 1=1 делает условие истинным, а -- комментирует остаток запроса, чтобы не получить синтаксическую ошибку.
Таким образом, злоумышленник обманывает приложение и заставляет его выполнить запрос так, что условие 1=1 всегда истинно, что приводит к возвращению всех записей из таблицы пользователей, независимо от того, правильный ли пароль был введен.
━━━━━━━━━━ Какие бывают SQLi ━━━━━━━━━━
Если коротко, SQL-инъекции можно разделить на видимые и слепые.
➤ UNION-based SQLi Когда результат запроса возвращается в ответе, мы можем использовать UNION, чтобы добавить к легитимной выборке данные из других таблиц.
➤ Error-based SQLi Когда приложение или БД возвращают ошибки, через них можно вытягивать структуру или данные.
➤ Boolean-based blind SQLi Когда данные не отображаются напрямую, но можно задавать вопросы в стиле “истина / ложь” и по разнице в ответе постепенно восстанавливать данные.
➤ Time-based blind SQLi Похожая история с булевым значением, но вместо разницы в содержимом используется задержка, например через sleep.
➤ Out-of-band SQLi Когда результат уходит не в HTTP-ответ, а через внешний канал, например DNS/HTTP-запрос от БД на контролируемый сервер.
━━━━━━━━━━ Где изучать глубже ━━━━━━━━━━
Во все времена и все советуют ресурс — PortSwigger Web Security Academy.
Там можно не просто прочитать про SQLi, но и порешать лабы: ➥ https://portswigger.net/web-security/sql-injection
━━━━━━━━━━ Главное заблуждение при фиксе ━━━━━━━━━━
Много где пишут: “используйте параметризованные запросы”. И это от части правильный совет.
Но важно понимать нюанс: ➤ параметры защищают значения, но не защищают структуру SQL-запроса.
То есть вот так — хорошо: • WHERE username = ?
А вот так — уже плохо: • ORDER BY + userInput • SELECT * FROM + tableName • WHERE + rawCondition
Проблема появляется там, где пользовательский ввод снова начинает влиять на саму структуру SQL.
━━━━━━━━━━ Где параметризация ломается ━━━━━━━━━━
➤ Динамический ORDER BY Плохая идея: • ORDER BY + sort Если пользователь может передать sort, он влияет на структуру запроса.
➤ Динамическое имя таблицы или колонки Плохо: • SELECT * FROM + tableName Параметры обычно не предназначены для подстановки имён таблиц и колонок.
➤ Часть WHERE собирается строкой Плохо: • WHERE status = ? AND + filter Даже если часть запроса параметризована, небезопасный кусок строки может вернуть SQLi обратно.
➤ Stored Procedures не всегда безопасны Stored procedure сама по себе не гарантирует защиту. Если внутри процедуры есть dynamic SQL и склейка строк, проблема остаётся: • EXEC('SELECT * FROM users WHERE name = ''' + @name + '''')
➤ ORM тоже не панацея ORM безопасен, пока используется его нормальные методы. Но как только начинается: • raw() • createQuery() • query("SELECT ... " + userInput) то мы вновь собираем SQL запрос и можем получить ту же самую SQL-инъекцию.
━━━━━━━━━━ Как защищаться нормально ━━━━━━━━━━
Минимальный набор: • параметризованные запросы для значений • никакой склейки пользовательского ввода с SQL • белые списки ввода пользователя • осторожность с raw SQL и stored procedures • минимум прав у пользователя БД • тесты на инъекции в критичных ручках
#AppSec #SQLi #SQLInjection #Pentest #CyberSecurity #OWASP #Portswigger #IT