Разберите пример SQL-инъекции: "SELECT * FROM users WHERE name = '" + user + "';" — объясните уязвимость и способы защиты на уровне языка и базы данных
Кратко о проблеме - Уязвимость: строковая конкатенация запроса с пользовательским вводом позволяет атакующему подставить SQL-код. Пример уязвимого кода: SELECT * FROM users WHERE name = '" + user + "';"; - Если user="′OR′1′=′1"user = "' OR '1'='1" user="′OR′1′=′1", итоговый запрос станет: SELECT * FROM users WHERE name = '' OR '1'='1'; — это логическая тавтология, вернёт все строки (возможна утечка данных, обход аутентификации и т.д.). Типичные приёмы атак - Тавтологии: получить все записи (пример выше). - UNION-инъекции: добавить свой SELECT и вытянуть таблицы. - Stacked queries (несколько запросов): выполнить DROP/INSERT/UPDATE (если СУБД и драйвер это допускают). - Blind injections (boolean/time-based): выстраивать побайтовые утечки через ответы или задержки. Защита на уровне языка (код приложения) 1. Параметризованные запросы / prepared statements (главный метод). - Java (JDBC): PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?"); ps.setString(1, user); ResultSet rs = ps.executeQuery(); - PHP (PDO): $stmt = $pdo->prepare("SELECT * FROM users WHERE name = ?"); $stmt->execute([$user]); - Python (psycopg2): cur.execute("SELECT * FROM users WHERE name = %s", (user,)) 2. Использовать ORM или query builder — они автоматически параметризуют значения (правда, будьте осторожны при вставке сырых SQL-фрагментов). 3. Валидация и белые списки: проверять формат/длину/символы входа (особенно для идентификаторов: имён таблиц/полей, которых нельзя параметризовать). 4. Экранирование — только как запасной вариант; легко ошибиться и небезопасно по сравнению с параметризацией. 5. Не собирать SQL динамически из пользовательских данных (особенно идентификаторы). Если нужно, разрешать только предопределённые варианты (whitelist). 6. Логирование и ограничение ввода по длине. Защита на уровне базы данных и инфраструктуры 1. Минимальные привилегии: аккаунт приложения должен иметь только нужные права (только SELECT/INSERT/UPDATE нужных таблиц). 2. Отключить поддержку выполнения нескольких команд в одном запросе, если СУБД/драйвер поддерживает настройку (во избежание stacked queries). 3. Использовать stored procedures правильно: если внутри они строят SQL через конкатенацию — всё ещё уязвимы; готовые параметризованные процедуры безопаснее. 4. Ролевой доступ, представления (views), Row-Level Security — ограничивают доступ к данным. 5. WAF/IDS/IPS — дополнительный слой защиты и обнаружения атак. 6. Аудит и мониторинг подозрительных запросов и ошибок. Короткая сводка (чем защищаться в первую очередь) - Всегда: параметризованные запросы / prepared statements. - Дополнительно: минимальные права, валидация входа, whitelist для идентификаторов, мониторинг/WAF. Если нужно, могу показать конкретный пример безопасной реализации в вашем языке/СУБД.
- Уязвимость: строковая конкатенация запроса с пользовательским вводом позволяет атакующему подставить SQL-код. Пример уязвимого кода:
SELECT * FROM users WHERE name = '" + user + "';";
- Если user="′OR′1′=′1"user = "' OR '1'='1" user="′OR′1′=′1", итоговый запрос станет:
SELECT * FROM users WHERE name = '' OR '1'='1';
— это логическая тавтология, вернёт все строки (возможна утечка данных, обход аутентификации и т.д.).
Типичные приёмы атак
- Тавтологии: получить все записи (пример выше).
- UNION-инъекции: добавить свой SELECT и вытянуть таблицы.
- Stacked queries (несколько запросов): выполнить DROP/INSERT/UPDATE (если СУБД и драйвер это допускают).
- Blind injections (boolean/time-based): выстраивать побайтовые утечки через ответы или задержки.
Защита на уровне языка (код приложения)
1. Параметризованные запросы / prepared statements (главный метод).
- Java (JDBC):
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?");
ps.setString(1, user);
ResultSet rs = ps.executeQuery();
- PHP (PDO):
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = ?");
$stmt->execute([$user]);
- Python (psycopg2):
cur.execute("SELECT * FROM users WHERE name = %s", (user,))
2. Использовать ORM или query builder — они автоматически параметризуют значения (правда, будьте осторожны при вставке сырых SQL-фрагментов).
3. Валидация и белые списки: проверять формат/длину/символы входа (особенно для идентификаторов: имён таблиц/полей, которых нельзя параметризовать).
4. Экранирование — только как запасной вариант; легко ошибиться и небезопасно по сравнению с параметризацией.
5. Не собирать SQL динамически из пользовательских данных (особенно идентификаторы). Если нужно, разрешать только предопределённые варианты (whitelist).
6. Логирование и ограничение ввода по длине.
Защита на уровне базы данных и инфраструктуры
1. Минимальные привилегии: аккаунт приложения должен иметь только нужные права (только SELECT/INSERT/UPDATE нужных таблиц).
2. Отключить поддержку выполнения нескольких команд в одном запросе, если СУБД/драйвер поддерживает настройку (во избежание stacked queries).
3. Использовать stored procedures правильно: если внутри они строят SQL через конкатенацию — всё ещё уязвимы; готовые параметризованные процедуры безопаснее.
4. Ролевой доступ, представления (views), Row-Level Security — ограничивают доступ к данным.
5. WAF/IDS/IPS — дополнительный слой защиты и обнаружения атак.
6. Аудит и мониторинг подозрительных запросов и ошибок.
Короткая сводка (чем защищаться в первую очередь)
- Всегда: параметризованные запросы / prepared statements.
- Дополнительно: минимальные права, валидация входа, whitelist для идентификаторов, мониторинг/WAF.
Если нужно, могу показать конкретный пример безопасной реализации в вашем языке/СУБД.