Проанализируйте SQL-запрос: String q = "SELECT * FROM users WHERE name = '"+name+"'"; — объясните уязвимость, возможные атаки и как правильно защититься, учитывая разные СУБД
Уязвимость - Запрос String q = "SELECT * FROM users WHERE name = '"+name+"'"; — это классический SQL-инъекционный vuln: пользовательский ввод вставляется в SQL как текст, без разделения кода и данных, поэтому злоумышленник может изменить структуру запроса. Примеры атак (коротко) - Аутентификация/обход фильтров: ввод name = "' OR '111' = '111'" приведёт к условию, всегда истинному. - Удаление/изменение данных (если БД/драйвер поддерживает несколько запросов): name = "'; DROP TABLE users; --" - UNION-экстракция данных: name = "' UNION SELECT username, password FROM admins --" (нужна корректность числа/типа колонок) - Blind / time-based SQLi: - MySQL: name = "' OR IF( (SUBSTR(password,1,1)='a'), SLEEP(555), 0) --" - PostgreSQL: use pg_sleep(555) - MSSQL: use WAITFOR DELAY '00:00:05' - Error-based и Out-of-band (через DNS/HTTP) для вытягивания больших объёмов данных. Особенности по СУБД - MySQL: комментарии `-- `, `#`, `/* */`; функция задержки SLEEP(nnn). По умолчанию мульти-запросы отключены в некоторых драйверах (нужно избегать enableMultiQueries). - PostgreSQL: `--`, `/* */`; задержка pg_sleep(nnn). Параметры через libpq/psycopg2 защищают. - MSSQL: `--`, `/* */`; WAITFOR DELAY; поддерживает batch-запросы — опасно, если конкатенация. - Oracle: `--`, `/* */`; задержки реализуются через dbms_lock.sleep(nnn) в PL/SQL; синтаксис и функции другие. (Во всех СУБД синтаксис инъекционных полезных нагрузок отличается — применять безопасные практики универсально.) Как правильно защищаться (приоритеты) 1. Параметризованные запросы / prepared statements (самый надёжный способ): - Java (JDBC): - String q = "SELECT * FROM users WHERE name = ?"; - PreparedStatement ps = conn.prepareStatement(q); - ps.setString(1, name); - PHP (PDO): - $stmt = $pdo->prepare('SELECT * FROM users WHERE name = :name'); - $stmt->execute(['name' => $name]); - MSSQL: используйте параметры или EXEC sp_executesql с параметрами: - EXEC sp_executesql N'SELECT * FROM users WHERE name = @name', N'@name nvarchar(100)', @name = N'...' Примечание: отключите эмуляцию prepared statements у драйверов (например, PDO::ATTR_EMULATE_PREPARES = false), чтобы использовать native prepares. 2. Избегайте динамической конкатенации SQL — даже внутри хранимых процедур. Хранимые процедуры безопасны только если они принимают параметры и не строят SQL строк динамически. 3. Ограничение прав (least privilege): учётная запись БД для приложения должна иметь минимальные привилегии (SELECT/INSERT/UPDATE только на нужные таблицы). Запрещать DDL/DROP из приложения. 4. Валидация и белый список для идентификаторов: имена таблиц/колонок нельзя параметризовать; если нужно выбирать таблицу по имени — используйте сопоставление через фиксированный список (whitelist). 5. Корректное экранирование — только как резервный вариант, если параметризация невозможна. Используйте специализированные API (например, mysqli_real_escape_string для MySQL старых приложений) и корректную кодировку (utf8/utf8mb4). Экранирование легко промахнуть; не предпочтительно. 6. Защита на уровне инфраструктуры: WAF, ограничение сетевых возможностей БД, мониторинг/логирование аномалий запросов. Дополнительные рекомендации - Проверяйте и нормализуйте кодировки (проблемы с многобайтовыми кодировками могут позволить обход экранирования). - Будьте внимательны к second-order SQLi: безопасно вставлённые данные могут быть позже использованы в небезопасном конкатенируемом контексте. - Минимизируйте возвращаемые данные (не SELECT *), используйте пагинацию и лимиты. - Периодически проводить тестирование безопасности (SAST/DAST, pentest). Краткий итог - Причина: конкатенация данных в SQL. - Лучшее решение: всегда использовать параметризованные запросы / prepared statements и минимальные привилегии. Для каждой СУБД — применять её нативные механизмы параметров и учитывать особенности (например, функции задержки и синтаксис комментариев), но принцип один и тот же.
- Запрос String q = "SELECT * FROM users WHERE name = '"+name+"'"; — это классический SQL-инъекционный vuln: пользовательский ввод вставляется в SQL как текст, без разделения кода и данных, поэтому злоумышленник может изменить структуру запроса.
Примеры атак (коротко)
- Аутентификация/обход фильтров: ввод name = "' OR '111' = '111'" приведёт к условию, всегда истинному.
- Удаление/изменение данных (если БД/драйвер поддерживает несколько запросов): name = "'; DROP TABLE users; --"
- UNION-экстракция данных: name = "' UNION SELECT username, password FROM admins --" (нужна корректность числа/типа колонок)
- Blind / time-based SQLi:
- MySQL: name = "' OR IF( (SUBSTR(password,1,1)='a'), SLEEP(555), 0) --"
- PostgreSQL: use pg_sleep(555)
- MSSQL: use WAITFOR DELAY '00:00:05'
- Error-based и Out-of-band (через DNS/HTTP) для вытягивания больших объёмов данных.
Особенности по СУБД
- MySQL: комментарии `-- `, `#`, `/* */`; функция задержки SLEEP(nnn). По умолчанию мульти-запросы отключены в некоторых драйверах (нужно избегать enableMultiQueries).
- PostgreSQL: `--`, `/* */`; задержка pg_sleep(nnn). Параметры через libpq/psycopg2 защищают.
- MSSQL: `--`, `/* */`; WAITFOR DELAY; поддерживает batch-запросы — опасно, если конкатенация.
- Oracle: `--`, `/* */`; задержки реализуются через dbms_lock.sleep(nnn) в PL/SQL; синтаксис и функции другие.
(Во всех СУБД синтаксис инъекционных полезных нагрузок отличается — применять безопасные практики универсально.)
Как правильно защищаться (приоритеты)
1. Параметризованные запросы / prepared statements (самый надёжный способ):
- Java (JDBC):
- String q = "SELECT * FROM users WHERE name = ?";
- PreparedStatement ps = conn.prepareStatement(q);
- ps.setString(1, name);
- PHP (PDO):
- $stmt = $pdo->prepare('SELECT * FROM users WHERE name = :name');
- $stmt->execute(['name' => $name]);
- MSSQL: используйте параметры или EXEC sp_executesql с параметрами:
- EXEC sp_executesql N'SELECT * FROM users WHERE name = @name', N'@name nvarchar(100)', @name = N'...'
Примечание: отключите эмуляцию prepared statements у драйверов (например, PDO::ATTR_EMULATE_PREPARES = false), чтобы использовать native prepares.
2. Избегайте динамической конкатенации SQL — даже внутри хранимых процедур. Хранимые процедуры безопасны только если они принимают параметры и не строят SQL строк динамически.
3. Ограничение прав (least privilege): учётная запись БД для приложения должна иметь минимальные привилегии (SELECT/INSERT/UPDATE только на нужные таблицы). Запрещать DDL/DROP из приложения.
4. Валидация и белый список для идентификаторов: имена таблиц/колонок нельзя параметризовать; если нужно выбирать таблицу по имени — используйте сопоставление через фиксированный список (whitelist).
5. Корректное экранирование — только как резервный вариант, если параметризация невозможна. Используйте специализированные API (например, mysqli_real_escape_string для MySQL старых приложений) и корректную кодировку (utf8/utf8mb4). Экранирование легко промахнуть; не предпочтительно.
6. Защита на уровне инфраструктуры: WAF, ограничение сетевых возможностей БД, мониторинг/логирование аномалий запросов.
Дополнительные рекомендации
- Проверяйте и нормализуйте кодировки (проблемы с многобайтовыми кодировками могут позволить обход экранирования).
- Будьте внимательны к second-order SQLi: безопасно вставлённые данные могут быть позже использованы в небезопасном конкатенируемом контексте.
- Минимизируйте возвращаемые данные (не SELECT *), используйте пагинацию и лимиты.
- Периодически проводить тестирование безопасности (SAST/DAST, pentest).
Краткий итог
- Причина: конкатенация данных в SQL.
- Лучшее решение: всегда использовать параметризованные запросы / prepared statements и минимальные привилегии. Для каждой СУБД — применять её нативные механизмы параметров и учитывать особенности (например, функции задержки и синтаксис комментариев), но принцип один и тот же.