
SQL-инъекция (SQL Injection) — это распространённая и весьма опасная уязвимость веб-приложений. Она позволяет злоумышленнику вмешиваться в SQL-запросы, исполняемые на сервере, и получить несанкционированный доступ к данным, изменить их или даже удалить. При этом, злоумышленнику часто достаточно иметь доступ к фронт-енд сайта или приложения, где он экспериментирует с передаваемыми на сервер параметрами.
Этот процесс также не сложно автоматизировать.
В этой статье мы разберём:
- что такое SQL-инъекция и как она работает;
- классические примеры на PHP;
- как защититься от уязвимости;
- безопасные практики написания кода.
На своей практике я не раз сталкивался с кодом, который содержал данную уязвимость. Чаще всего это были сайты, изготовленные на самописных движках, разработанные любителями-одиночками или небольшими студиями.
Что такое SQL-инъекция
SQL-инъекция — это метод атаки, при котором в параметры SQL-запроса внедряется вредоносный SQL-код. Если параметры (GET, POST) подставляются в SQL-запрос без проверки и экранирования, злоумышленник может, например:
- получить логины и пароли;
- обойти авторизацию;
- удалить таблицы базы данных.
Пример уязвимого SQL-запроса на PHP.
1 2 3 4 5 6 7 8 9 |
<?php // Подключение к базе данных (упрощено) $conn = mysqli_connect("localhost", "user", "password", "db"); // Получаем имя пользователя из GET-запроса $username = $_GET['username']; $query = "SELECT * FROM users WHERE username = '$username'"; $result = mysqli_query($conn, $query); |
Если пользователь передаст в URL
1 |
?username=' OR 1=1 -- |
Запрос на сервере превратится в:
1 2 |
SELECT * FROM users WHERE username = '' OR 1=1 --' |
Т.е. вместо фильтрации по логину, мы запросим полный список пользователей.
Таким не хитрым образом, можно изменить логику работы, запросить данные, которые не должны были быть запрошены (и показаны). Если внедриться в INSERT или DELETE запросы, то можно удалить или вставить какие то свои данные, от лица другого пользователя и т.п.
А если в коде используется другой вариант выполнения SQL, который позволяет исполнить сразу несколько запросов:
1 |
$result = mysqli_multi_query($conn, $query); |
То инъекции выходят на совсем иной уровень, т.к. открывается возможность внедрять вообще любые запросы:
1 |
?username='; DROP TABLE users;-- |
И безобидный SQL SELECT дополниться удалением целой таблицы:
1 2 |
SELECT * FROM users WHERE username = ''; DROP TABLE users;-- |
Как защититься: основные принципы
Не вставляйте данные напрямую в SQL
Никогда не подставляйте пользовательский ввод в SQL-запросы «как есть». Любые данные, полученные от пользователя, нужно проверять, очищать и подготавливать.
(Принцип нулевого доверия ко входным данным.)
Используйте подготовленные выражения (Prepared Statements)
Этот основной способ защиты — следует из первого пункта. Пример с использованием MySQLi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php $conn = mysqli_connect("localhost", "user", "password", "db"); $name = $_GET['name']; $message = $_GET['message']; $stmt = $conn->prepare(" INSERT INTO feedback (name, message) VALUES (?, ?) "); // "ss" — строка и еще строка $stmt->bind_param("ss", $_GET['name'], $_GET['message']); $stmt->execute(); $result = $stmt->get_result(); |
Или на PDO:
1 2 3 4 5 6 7 8 |
<?php $pdo = new PDO('mysql:host=localhost;dbname=db', 'user', 'password'); $stmt = $pdo->prepare(" SELECT * FROM users WHERE username = :username"); $stmt->execute(['username' => $_GET['username']]); $result = $stmt->fetchAll(); |
Подготовленные выражения автоматически экранируют значения и предотвращают SQL-инъекции.
Используйте минимальные права доступа к базе
У аккаунта базы данных не должно быть прав на DROP
, DELETE
без необходимости.
Не показывайте SQL-ошибки пользователю
Они могут подсказать злоумышленнику структуру базы.
1 |
mysqli_report(MYSQLI_REPORT_OFF); |
SQL-инъекции — это не «старомодная» угроза, а реальный риск для любого сайта, который работает с базой данных. К счастью, защита несложна: используйте подготовленные выражения, фильтрацию ввода и соблюдайте базовую гигиену кода.
Помните: чем проще запрос и чем меньше доверия к пользовательским данным — тем безопаснее ваше приложение.
Современные сайты чаще всего получают данную уязвимость вместе с дополнительными плагинами, которые были созданы программистами-любителями. Потому нужно своевременно обновлять и код движка и устанавливать обновления плагинов.
А если какой то плагин не используется, рекомендуется отключать и удалять его файлы. Перефразируя известную песню — no plugin — no cry.