Найдите уязвимость в этом SQL-подходе к аутентификации: query = "SELECT * FROM users WHERE login = '" + login + "' AND pass = '" + hash + "'"; — объясните виды атак, как их использовать, и приведите безопасные альтернативы в разных СУБД и ORM; обсудите также защиту от утечек через логи и ошибок конфигурации
Кратко — это уязвимость типа SQL‑инъекции: конкатенация строк с входными данными позволяет злоумышленнику подменять структуру запроса и выполнять произвольные SQL‑операции (вход в чужой аккаунт, чтение/удаление данных и т. д.). Почему именно: строковая сборка query = "SELECT * FROM users WHERE login = '" + login + "' AND pass = '" + hash + "'"; вставляет непроверённые символы пользователя прямо в SQL, поэтому если в поле login придёт специальная нагрузка, итоговый SQL изменится. Виды атак и примеры (как использовать) - Аутентификация bypass (простая): подставить payload, который делает условие истинным: login: ' OR '1'='1' -- Итоговый WHERE станет примерно: WHERE login = '' OR '1'='1' --' AND ... (′1′=′1′'1'='1'′1′=′1′ — всегда истинно). - Логическое/булево‑слепое извлечение: проверять по битам/символам значения из БД, например: ' OR (SUBSTR((SELECT pass FROM users WHERE login='admin'),1,1)='a') -- или time‑based через SLEEP: ' OR IF(SUBSTR((SELECT pass FROM users WHERE login='admin'),1,1)='a', SLEEP(5),0) -- (задержка 555 секунд сигнализирует об истинности). - UNION‑инъекция (извлечение данных): закрыть логический фильтр и добавить UNION SELECT для чтения других таблиц. - Stacked queries (несколько команд в одном запросе): ; DROP TABLE users; — работает если СУБД/драйвер разрешают множественные инструкции. - Error‑based (через ошибки): вынудить БД выдать текст ошибки с нужными данными. - Комментарии и обход фильтров: разные СУБД допускают разные комментарии (`--`, `#`, `/* ... */`). Особенности по СУБД (кратко) - MySQL: комментарий `-- ` (нужен пробел), `#`, поддерживает SLEEP(), часто допускает multi‑statements если включены. - PostgreSQL: `--`, `/* */`, поддерживает pg_sleep(). - MSSQL: `--`, `/* */`, поддерживает `;` и часто multi‑statements; имеет WAITFOR DELAY для тайм‑задержки. - Oracle: `--`, `/* */`, PL/SQL блоки; payloadы отличаются синтаксисом. Безопасные альтернативы (рекомендуемые) 1) Параметризованные запросы / подготовленные выражения (bind parameters) - PHP PDO: $stmt = $pdo->prepare("SELECT * FROM users WHERE login = ? AND pass = ?"); $stmt->execute([$login, $hash]); - Python (psycopg2 для PostgreSQL): cur.execute("SELECT * FROM users WHERE login = %s AND pass = %s", (login, hash)) - Java JDBC: PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE login = ? AND pass = ?"); ps.setString(1, login); ps.setString(2, hash); - C# (SqlCommand): cmd.CommandText = "SELECT * FROM users WHERE login = @login AND pass = @pass"; cmd.Parameters.AddWithValue("@login", login); Важно: не вставлять значения в строку запроса вручную. 2) ORM‑абстракции (правильное использование) - Django ORM: user = User.objects.filter(login=login).first() - SQLAlchemy: session.query(User).filter(User.login == login).one_or_none() - Entity Framework / Hibernate: использовать параметризованные LINQ/Criteria, не строить SQL вручную. ORM безопасны при обычном использовании; опасны при вызове raw SQL без параметров. 3) Хранение паролей и сравнение - Хранить только соль + сильный хеш: bcrypt, Argon2, PBKDF2 (предпочтительнее Argon2/bcrypt). - Не сравнивать пароли в SQL. Лучший паттерн: 1. Выбрать хеш по логину: SELECT pass_hash, salt FROM users WHERE login = ?; 2. В приложении вычислить hashCandidate = bcrypt(password + salt) и сравнить безопасно (constant‑time). - Использовать готовые библиотеки для проверки пароля (они уже делают constant‑time compare). 4) Ограничения на уровне БД и конфигурация - Отключить multi‑statement (если не нужен). - Выдавать приложению минимально необходимые привилегии (например, SELECT на таблице users). - Включить аудит/логирование подозрительных запросов, но не логировать секреты. - Применять WAF/IDS для обнаружения подозрительных паттернов. Защита от утечек через логи и ошибки конфигурации - Не логируйте сырые запросы с подставленными параметрами и тем более не логируйте пароли/хеши. Логируйте только метаданные (категория ошибки, временная метка, ID запроса), параметры — в маскированном виде. - В логах и трассировках заменяйте/редактируйте чувствительные поля (masking/redaction). - Не показывайте подробные SQL‑ошибки пользователю — используйте пользовательские страницы ошибок и централизованный сбор ошибок (с контролем доступа). - Ограничьте уровень логирования в проде (ERROR/WARN вместо DEBUG). - Контролируйте конфигурационные файлы: не держите в репозитории учетные данные, используйте секрет‑менеджер. - Ротация и минимизация прав доступа: смена паролей/ключей, MFA для админов. - Мониторинг/Alerting: аномальные запросы, частые проваленные логины, большие UNION/селекты — триггерить расследование. Краткий чек‑лист защиты (порядок приоритетов) - Использовать параметризованные запросы / ORM без raw SQL. - Хранить пароли в виде bcrypt/Argon2 хешей, проверять в приложении. - Минимальные привилегии для БД-пользователя. - Отключить multi‑statements, если не нужны. - Не логировать секреты; редактировать/маскировать параметры в логах. - Не выводить стек‑трэйсы и подробные ошибки клиентам. - Внедрить WAF, мониторинг и аудит. Если нужно — пришлю конкретные примеры защищённого кода для выбранного языка/СУБД или демонстрации эксплойта (payload'ы) для вашей СУБД.
Почему именно: строковая сборка
query = "SELECT * FROM users WHERE login = '" + login + "' AND pass = '" + hash + "'";
вставляет непроверённые символы пользователя прямо в SQL, поэтому если в поле login придёт специальная нагрузка, итоговый SQL изменится.
Виды атак и примеры (как использовать)
- Аутентификация bypass (простая): подставить payload, который делает условие истинным:
login: ' OR '1'='1' --
Итоговый WHERE станет примерно: WHERE login = '' OR '1'='1' --' AND ...
(′1′=′1′'1'='1'′1′=′1′ — всегда истинно).
- Логическое/булево‑слепое извлечение: проверять по битам/символам значения из БД, например:
' OR (SUBSTR((SELECT pass FROM users WHERE login='admin'),1,1)='a') --
или time‑based через SLEEP:
' OR IF(SUBSTR((SELECT pass FROM users WHERE login='admin'),1,1)='a', SLEEP(5),0) --
(задержка 555 секунд сигнализирует об истинности).
- UNION‑инъекция (извлечение данных): закрыть логический фильтр и добавить UNION SELECT для чтения других таблиц.
- Stacked queries (несколько команд в одном запросе): ; DROP TABLE users; — работает если СУБД/драйвер разрешают множественные инструкции.
- Error‑based (через ошибки): вынудить БД выдать текст ошибки с нужными данными.
- Комментарии и обход фильтров: разные СУБД допускают разные комментарии (`--`, `#`, `/* ... */`).
Особенности по СУБД (кратко)
- MySQL: комментарий `-- ` (нужен пробел), `#`, поддерживает SLEEP(), часто допускает multi‑statements если включены.
- PostgreSQL: `--`, `/* */`, поддерживает pg_sleep().
- MSSQL: `--`, `/* */`, поддерживает `;` и часто multi‑statements; имеет WAITFOR DELAY для тайм‑задержки.
- Oracle: `--`, `/* */`, PL/SQL блоки; payloadы отличаются синтаксисом.
Безопасные альтернативы (рекомендуемые)
1) Параметризованные запросы / подготовленные выражения (bind parameters)
- PHP PDO:
$stmt = $pdo->prepare("SELECT * FROM users WHERE login = ? AND pass = ?");
$stmt->execute([$login, $hash]);
- Python (psycopg2 для PostgreSQL):
cur.execute("SELECT * FROM users WHERE login = %s AND pass = %s", (login, hash))
- Java JDBC:
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE login = ? AND pass = ?");
ps.setString(1, login); ps.setString(2, hash);
- C# (SqlCommand):
cmd.CommandText = "SELECT * FROM users WHERE login = @login AND pass = @pass";
cmd.Parameters.AddWithValue("@login", login);
Важно: не вставлять значения в строку запроса вручную.
2) ORM‑абстракции (правильное использование)
- Django ORM:
user = User.objects.filter(login=login).first()
- SQLAlchemy:
session.query(User).filter(User.login == login).one_or_none()
- Entity Framework / Hibernate: использовать параметризованные LINQ/Criteria, не строить SQL вручную.
ORM безопасны при обычном использовании; опасны при вызове raw SQL без параметров.
3) Хранение паролей и сравнение
- Хранить только соль + сильный хеш: bcrypt, Argon2, PBKDF2 (предпочтительнее Argon2/bcrypt).
- Не сравнивать пароли в SQL. Лучший паттерн:
1. Выбрать хеш по логину: SELECT pass_hash, salt FROM users WHERE login = ?;
2. В приложении вычислить hashCandidate = bcrypt(password + salt) и сравнить безопасно (constant‑time).
- Использовать готовые библиотеки для проверки пароля (они уже делают constant‑time compare).
4) Ограничения на уровне БД и конфигурация
- Отключить multi‑statement (если не нужен).
- Выдавать приложению минимально необходимые привилегии (например, SELECT на таблице users).
- Включить аудит/логирование подозрительных запросов, но не логировать секреты.
- Применять WAF/IDS для обнаружения подозрительных паттернов.
Защита от утечек через логи и ошибки конфигурации
- Не логируйте сырые запросы с подставленными параметрами и тем более не логируйте пароли/хеши. Логируйте только метаданные (категория ошибки, временная метка, ID запроса), параметры — в маскированном виде.
- В логах и трассировках заменяйте/редактируйте чувствительные поля (masking/redaction).
- Не показывайте подробные SQL‑ошибки пользователю — используйте пользовательские страницы ошибок и централизованный сбор ошибок (с контролем доступа).
- Ограничьте уровень логирования в проде (ERROR/WARN вместо DEBUG).
- Контролируйте конфигурационные файлы: не держите в репозитории учетные данные, используйте секрет‑менеджер.
- Ротация и минимизация прав доступа: смена паролей/ключей, MFA для админов.
- Мониторинг/Alerting: аномальные запросы, частые проваленные логины, большие UNION/селекты — триггерить расследование.
Краткий чек‑лист защиты (порядок приоритетов)
- Использовать параметризованные запросы / ORM без raw SQL.
- Хранить пароли в виде bcrypt/Argon2 хешей, проверять в приложении.
- Минимальные привилегии для БД-пользователя.
- Отключить multi‑statements, если не нужны.
- Не логировать секреты; редактировать/маскировать параметры в логах.
- Не выводить стек‑трэйсы и подробные ошибки клиентам.
- Внедрить WAF, мониторинг и аудит.
Если нужно — пришлю конкретные примеры защищённого кода для выбранного языка/СУБД или демонстрации эксплойта (payload'ы) для вашей СУБД.