Найдите уязвимость в PHP‑фрагменте: $id = $_GET['id']; $sql = "SELECT * FROM users WHERE id = $id"; $res = mysqli_query($conn,$sql); Объясните риски SQL‑инъекции, варианты атак и исправьте код с подготовленными выражениями и проверкой входа
Уязвимость: в строке ```php $id = $_GET['id']; $sql = "SELECT * FROM users WHERE id = $id"; $res = mysqli_query($conn,$sql); ``` входной параметр `id` напрямую подставляется в SQL — это классическая SQL‑инъекция (SQLi). Риски SQL‑инъекции (кратко): - обход авторизации и чтение всех записей (например: payload `1 OR 1=1`); - извлечение конфиденциальных данных через `UNION`/error/boolean/time‑based техники; - изменение или удаление данных (`UPDATE`, `DELETE`) при возможности выполнения нескольких операторов; - удалённое выполнение длительных запросов (DoS) через `SLEEP(...)`; - компрометация сервера при наличии у БД расширений. Примеры атакующих полезных нагрузок (payloads): - Базовый обход фильтра: 1 OR 1=11\ \text{OR}\ 1=11OR1=1 -> в итоге `WHERE id = 1 OR 1=1` возвращает все строки. - UNION‑экстракция: 0 UNION SELECT username,password FROM users0\ \text{UNION}\ \text{SELECT}\ username, password\ \text{FROM}\ users0UNIONSELECTusername,passwordFROMusers
- Time‑based blind: 1 AND SLEEP(5)1\ \text{AND}\ \text{SLEEP}(5)1ANDSLEEP(5) — задержка подтверждает уязвимость. (замечание: конкретный синтаксис и возможности зависят от СУБД и конфигурации.) Как исправить — два обязательных шага: проверка/валидация входа + подготовленные выражения (prepared statements). Правильный пример (mysqli, ожидается целочисленный id): ```php // Валидация id как целого $id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT); if ($id === false || $id === null) { http_response_code(400); exit('Invalid id'); } // Подготовленное выражение $stmt = $conn->prepare("SELECT * FROM users WHERE id = ?"); if ($stmt === false) { error_log($conn->error); http_response_code(500); exit('DB error'); } $stmt->bind_param("i", $id); // 'i' — integer $stmt->execute(); $res = $stmt->get_result(); $user = $res->fetch_assoc(); // далее обработка результата $stmt->close(); ``` Если `id` может быть строкой (например UUID), используйте `bind_param("s", $id)` и соответствующую валидацию (регэксп для формата UUID). Дополнительные рекомендации: - Никогда не формируйте SQL через конкатенацию непроверенных данных. - Минимизируйте права пользователя БД (нет прав на DROP/ALTER если не нужны). - Логи и мониторинг подозрительных входов. - Используйте ORM/абстракции или библиотеки, которые по умолчанию применяют подготовленные выражения. Итого: устраняйте уязвимость комбинацией строгой валидации входных данных и подготовленных выражений с привязкой параметров.
```php
$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE id = $id";
$res = mysqli_query($conn,$sql);
```
входной параметр `id` напрямую подставляется в SQL — это классическая SQL‑инъекция (SQLi).
Риски SQL‑инъекции (кратко):
- обход авторизации и чтение всех записей (например: payload `1 OR 1=1`);
- извлечение конфиденциальных данных через `UNION`/error/boolean/time‑based техники;
- изменение или удаление данных (`UPDATE`, `DELETE`) при возможности выполнения нескольких операторов;
- удалённое выполнение длительных запросов (DoS) через `SLEEP(...)`;
- компрометация сервера при наличии у БД расширений.
Примеры атакующих полезных нагрузок (payloads):
- Базовый обход фильтра: 1 OR 1=11\ \text{OR}\ 1=11 OR 1=1 -> в итоге `WHERE id = 1 OR 1=1` возвращает все строки.
- UNION‑экстракция: 0 UNION SELECT username,password FROM users0\ \text{UNION}\ \text{SELECT}\ username, password\ \text{FROM}\ users0 UNION SELECT username,password FROM users - Time‑based blind: 1 AND SLEEP(5)1\ \text{AND}\ \text{SLEEP}(5)1 AND SLEEP(5) — задержка подтверждает уязвимость.
(замечание: конкретный синтаксис и возможности зависят от СУБД и конфигурации.)
Как исправить — два обязательных шага: проверка/валидация входа + подготовленные выражения (prepared statements).
Правильный пример (mysqli, ожидается целочисленный id):
```php
// Валидация id как целого
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if ($id === false || $id === null) {
http_response_code(400);
exit('Invalid id');
}
// Подготовленное выражение
$stmt = $conn->prepare("SELECT * FROM users WHERE id = ?");
if ($stmt === false) {
error_log($conn->error);
http_response_code(500);
exit('DB error');
}
$stmt->bind_param("i", $id); // 'i' — integer
$stmt->execute();
$res = $stmt->get_result();
$user = $res->fetch_assoc();
// далее обработка результата
$stmt->close();
```
Если `id` может быть строкой (например UUID), используйте `bind_param("s", $id)` и соответствующую валидацию (регэксп для формата UUID).
Дополнительные рекомендации:
- Никогда не формируйте SQL через конкатенацию непроверенных данных.
- Минимизируйте права пользователя БД (нет прав на DROP/ALTER если не нужны).
- Логи и мониторинг подозрительных входов.
- Используйте ORM/абстракции или библиотеки, которые по умолчанию применяют подготовленные выражения.
Итого: устраняйте уязвимость комбинацией строгой валидации входных данных и подготовленных выражений с привязкой параметров.