SQL-инъекция (или «внедрение SQL-кода») — это метод получения несанкционированного доступа (разновидность взлома) к базе данных, при котором вредоносный код выполняется прямо из поля ввода в обычной форме.
Если SQL-инъекция прошла успешно, неавторизованные лица могут читать, создавать, обновлять или даже удалять записи из таблиц базы данных. Этот метод используется хакерами, пентестерами, тестировщиками и не только.
Пример №1: SQL-инъекция с использованием нескольких стейтментов
Предположим, у нас на сайте есть форма для поиска товаров по ID. Фрагмент PHP-кода поиска товара будет выглядеть примерно так:
1 2 3 |
$prod_id = $_GET["prod_id"]; $sql = "SELECT * FROM Products WHERE product_id = " . $prod_id; |
Если пользователь введет значение 20
, SQL интерпретирует это как:
1 |
SELECT * FROM Products WHERE product_id = 20 |
Ничего подозрительного, верно?
Но что, если пользователь введет 20; DROP TABLE Products;
? Давайте посмотрим, как SQL интерпретирует это:
1 |
SELECT * FROM Products WHERE product_id = 20; DROP TABLE Products; |
Данная команда удалит таблицу Products из базы данных. Это стало возможным, потому что большинство систем управления базами данных (СУБД) могут выполнять несколько команд одновременно.
Пример №2: SQL-инъекция с использованием всегда истинного условия
Другой способ выполнить SQL-инъекцию — это передать условие, которое всегда обрабатывается как true
, чтобы данные всегда извлекались, несмотря ни на что.
Давайте взглянем на другой фрагмент PHP-кода, где у нас есть форма входа на наш веб-сайт, и пользователям нужно указать свой логин и пароль.
1 2 3 4 |
$username = $_POST["username"]; $password = $_POST["password"]; $sql = "SELECT * FROM Users WHERE username = \"" . $username . "\" AND password = \"" . $password . "\""; |
Если пользователь введет логин как root
и пароль как pass
, то SQL интерпретирует это как:
1 |
SELECT * FROM Users WHERE username = "root" AND password = "pass" |
Данный фрагмент кода выглядит нормально, когда пользователь вводит корректные данные.
Но что делать, если пользователь введет имя пользователя как invalid_user" OR "1"="1
и пароль как invalid_pass" OR "1"="1
? Давайте посмотрим, как SQL отреагирует на это:
1 |
SELECT * FROM Users WHERE username = "invalid_user" OR "1"="1" AND password = "invalid_pass" OR "1"="1" |
Поскольку "1"="1"
всегда true
, независимо от того, какой логин и пароль введет пользователь, SQL извлечет всех пользователей из базы данных.
Как защититься от SQL-инъекций?
Способ №1: Валидация пользовательского ввода
Всегда следует проверять вводимые пользователем данные перед их фактической отправкой в базу данных. Хорошими практиками являются удаление пробелов, парсинг специальных символов, ограничение размера ввода и т.д.
Например:
1 2 3 4 5 |
$data = $_POST["name"]; $data = trim($data); $data = stripslashes($data); $data = htmlspecialchars($data); |
Данный фрагмент PHP-кода в некоторой степени «проверяет» входные данные.
Способ №2: Использование ORM
ORM (сокр. от «Object Relational Mapping») — это инструмент, который конвертирует SQL-команды в код языка программирования и наоборот.
При использовании ORM по большей части не нужно писать «чистый» SQL-код. Поскольку ORM разработаны с учетом хороших практик и протоколов безопасности, они предоставляют защиту, оставаясь при этом просты в использовании.
Например, следующий SQL-код:
1 |
SELECT * FROM Users WHERE id = 5; |
будет выглядеть как
1 |
select(Users).where(Users.id == 5) |
в SQLAlchemy ORM для Python-кода.
Способ №3: Использование подготовленных запросов
Подготовленные запросы — это SQL-код с плейсхолдерами. Переданные аргументы просто помещаются на место плейсхолдеров.
Например:
1 2 3 4 5 6 7 8 9 |
$sql = "INSERT INTO Users (first_name, last_name, email) VALUES (?, ?, ?)"; mysqli_stmt_bind_param($sql, "sss", $first_name, $last_name, $email); $first_name = "Harry"; $last_name = "Potter"; $email = "harrypotter@mail.com"; mysqli_stmt_execute($stmt); |
Здесь переданные значения помещаются на место ?
и структура SQL-кода сохраняется.
Заключение
SQL-инъекция — это очень распространенный способ взлома. При использовании «сырого» SQL-кода, его следует тщательно протестировать и проверить.
Еще одним альтернативным вариантом защиты при разработке приложений/сайтов является использование фреймворков (таких как Django, Laravel, ASP.NET и др.) вместо того, чтобы писать код с нуля. Фреймворки по умолчанию обрабатывают SQL-инъекции, а также решают многие другие часто возникающие проблемы.