Разберите уязвимость SQL-инъекции на примере PHP-кода: $query = "SELECT * FROM users WHERE name = '".$_GET['user']."'"; — как и почему это опасно, как исправить и какие общие практики предотвращения инъекций существуют
Почему опасно (коротко) - В коде происходит конкатенация пользовательского ввода в текст SQL-запроса: $query="SELECT∗FROMusersWHEREname=′".__."′"; \text{\$query} = "SELECT * FROM users WHERE name = '" . \_\_ . "'"; $query="SELECT∗FROMusersWHEREname=′".__."′";
При этом СУБД видит строку целиком и не отличает данные от кода — злоумышленник может подставить фрагмент SQL и изменить смысл запроса. - Последствия: чтение чужих записей, обход аутентификации, модификация или удаление данных, исполнение административных команд, получение доступа к серверу. Пример эксплуатации - Если пользователь передаст в \($_GET['user']\) значение: ′′OR′1′=′1′'' OR '1'='1'′′OR′1′=′1′ или "′OR′1′=′1""' OR '1'='1""′OR′1′=′1", то итоговый запрос станет: SELECT * FROM users WHERE name = '' OR '1'='1' и вернёт все строки (логический трюк 1=11=11=1). - Если передать "′;DROPTABLEusers;−−""'; DROP TABLE users; --""′;DROPTABLEusers;−−", получится: SELECT * FROM users WHERE name = ''; DROP TABLE users; --' — что может удалить таблицу. Как исправить (рекомендации и примеры) 1) Использовать подготовленные выражения (parameterized queries). Пример PDO: $pdo = new PDO(...); $stmt = $pdo->prepare("SELECT * FROM users WHERE name = ?"); $stmt->execute([$_GET['user']]); $user = $stmt->fetch(); Параметры передаются отдельно — СУБД трактует их как данные, а не как часть SQL. 2) Или MySQLi с привязкой: $stmt = $mysqli->prepare("SELECT * FROM users WHERE name = ?"); $stmt->bind_param("s", $_GET['user']); $stmt->execute(); $res = $stmt->get_result(); 3) Если нельзя использовать подготовленные выражения, как минимум экранировать: $safe = $mysqli->real_escape_string($_GET['user']); $query = "SELECT * FROM users WHERE name = '$safe'"; Но это хуже, требует аккуратности и правильной кодировки — предпочтительнее подготовленные запросы. 4) Для LIKE нужно экранировать спецсимволы (%, _) и передавать параметр в prepared statement. Общие практики предотвращения SQL-инъекций - Всегда использовать параметризованные запросы / подготовленные выражения. - Применять принцип наименьших привилегий: учётная запись БД должна иметь только нужные права (SELECT/INSERT и т. д.). - Валидация и белые списки для входных данных там, где возможно (например, числовые id — привести к целому). - Не хранить в SQL-составе необработанные идентификаторы/имена таблиц; если нужно, проверять по белому списку. - Логирование подозрительных запросов и мониторинг аномалий. - Регулярные обновления СУБД/Драйверов, применение WAF как дополнительный уровень защиты. - Использование ORM/Query Builder уменьшает риск, но не заменяет параметризацию при ручных запросах. - Кодирование вывода и защита от XSS/CSRF — сопутствующие меры безопасности. Коротко: никогда не вставляйте напрямую \($_GET\) или любой другой пользовательский ввод в SQL — используйте подготовленные запросы и принцип наименьших привилегий.
- В коде происходит конкатенация пользовательского ввода в текст SQL-запроса:
$query="SELECT∗FROMusersWHEREname=′".__."′"; \text{\$query} = "SELECT * FROM users WHERE name = '" . \_\_ . "'"; $query="SELECT∗FROMusersWHEREname=′".__."′"; При этом СУБД видит строку целиком и не отличает данные от кода — злоумышленник может подставить фрагмент SQL и изменить смысл запроса.
- Последствия: чтение чужих записей, обход аутентификации, модификация или удаление данных, исполнение административных команд, получение доступа к серверу.
Пример эксплуатации
- Если пользователь передаст в \($_GET['user']\) значение:
′′OR′1′=′1′'' OR '1'='1'′′OR′1′=′1′ или "′OR′1′=′1""' OR '1'='1""′OR′1′=′1",
то итоговый запрос станет:
SELECT * FROM users WHERE name = '' OR '1'='1'
и вернёт все строки (логический трюк 1=11=11=1).
- Если передать "′;DROPTABLEusers;−−""'; DROP TABLE users; --""′;DROPTABLEusers;−−", получится:
SELECT * FROM users WHERE name = ''; DROP TABLE users; --'
— что может удалить таблицу.
Как исправить (рекомендации и примеры)
1) Использовать подготовленные выражения (parameterized queries). Пример PDO:
$pdo = new PDO(...);
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = ?");
$stmt->execute([$_GET['user']]);
$user = $stmt->fetch();
Параметры передаются отдельно — СУБД трактует их как данные, а не как часть SQL.
2) Или MySQLi с привязкой:
$stmt = $mysqli->prepare("SELECT * FROM users WHERE name = ?");
$stmt->bind_param("s", $_GET['user']);
$stmt->execute();
$res = $stmt->get_result();
3) Если нельзя использовать подготовленные выражения, как минимум экранировать:
$safe = $mysqli->real_escape_string($_GET['user']);
$query = "SELECT * FROM users WHERE name = '$safe'";
Но это хуже, требует аккуратности и правильной кодировки — предпочтительнее подготовленные запросы.
4) Для LIKE нужно экранировать спецсимволы (%, _) и передавать параметр в prepared statement.
Общие практики предотвращения SQL-инъекций
- Всегда использовать параметризованные запросы / подготовленные выражения.
- Применять принцип наименьших привилегий: учётная запись БД должна иметь только нужные права (SELECT/INSERT и т. д.).
- Валидация и белые списки для входных данных там, где возможно (например, числовые id — привести к целому).
- Не хранить в SQL-составе необработанные идентификаторы/имена таблиц; если нужно, проверять по белому списку.
- Логирование подозрительных запросов и мониторинг аномалий.
- Регулярные обновления СУБД/Драйверов, применение WAF как дополнительный уровень защиты.
- Использование ORM/Query Builder уменьшает риск, но не заменяет параметризацию при ручных запросах.
- Кодирование вывода и защита от XSS/CSRF — сопутствующие меры безопасности.
Коротко: никогда не вставляйте напрямую \($_GET\) или любой другой пользовательский ввод в SQL — используйте подготовленные запросы и принцип наименьших привилегий.