Кратко: SQL‑инъекция — когда данные от пользователя попадают в SQL‑запрос как часть текста запроса; злоумышленник подставляет фрагменты SQL и меняет логику запроса. Принцип защиты — никогда не конкатенировать пользовательский ввод в SQL: использовать подготовленные выражения (parameterized queries), валидацию/фильтрацию, принцип наименьших привилегий и ограничение вывода ошибок. Пример уязвимого PHP (строгое конкатенирование ввода): ```php // уязвимый пример $id = $_GET['id']; // attacker: id=1 OR 1=1 $sql = "SELECT * FROM users WHERE id = $id"; $result = $conn->query($sql); ``` Безопасный вариант на PHP с PDO (подготовленные выражения + валидация): ```php // безопасно: PDO, подготовленные выражения и проверка $pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); // валидация: ожидали целое число $id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT); if ($id === false || $id === null) { // обработать ошибку запроса / вернуть 400 exit; } $stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id'); $stmt->execute([':id' => $id]); $user = $stmt->fetch(PDO::FETCH_ASSOC); ``` Пояснение: подготовленные выражения отделяют код запроса от данных; драйвер SQL экранирует/передаёт значение как параметр, поэтому ввод не может изменить синтаксис запроса. Пример уязвимого Node.js/JavaScript (сервер): ```js // уязвимый пример (mysql, конкатенация) const id = req.query.id; // attacker: id=1 OR 1=1 const sql = `SELECT * FROM users WHERE id = ${id}`; connection.query(sql, (err, rows) => { ... }); ``` Безопасный вариант на Node.js (параметры): ```js // безопасно: параметризованный запрос const id = parseInt(req.query.id, 10); if (Number.isNaN(id)) { // обработать ошибку return; } connection.query('SELECT * FROM users WHERE id = ?', [id], (err, rows) => { ... }); ``` Пояснение: placeholder (`?`) и массив параметров передают данные отдельно от SQL, предотвращая инъекцию. Коротко о дополнительных мерах предотвращения: - Валидировать и фильтровать входные данные (whitelist по формату, длине, типу). - Минимизировать привилегии учетной записи БД (чтение/запись только там, где нужно). - Не показывать подробные ошибки SQL пользователю. - Использовать ORM/абстракции и проверенные библиотеки, но не полагаться лишь на них. - Для динамических идентификаторов (имена таблиц/полей) — применять строгую белую выборку значений, не конкатенировать напрямую. Эти принципы вместе предотвращают большинство SQL‑инъекций.
Пример уязвимого PHP (строгое конкатенирование ввода):
```php
// уязвимый пример
$id = $_GET['id']; // attacker: id=1 OR 1=1
$sql = "SELECT * FROM users WHERE id = $id";
$result = $conn->query($sql);
```
Безопасный вариант на PHP с PDO (подготовленные выражения + валидация):
```php
// безопасно: PDO, подготовленные выражения и проверка
$pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
// валидация: ожидали целое число
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if ($id === false || $id === null) {
// обработать ошибку запроса / вернуть 400
exit;
}
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute([':id' => $id]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
```
Пояснение: подготовленные выражения отделяют код запроса от данных; драйвер SQL экранирует/передаёт значение как параметр, поэтому ввод не может изменить синтаксис запроса.
Пример уязвимого Node.js/JavaScript (сервер):
```js
// уязвимый пример (mysql, конкатенация)
const id = req.query.id; // attacker: id=1 OR 1=1
const sql = `SELECT * FROM users WHERE id = ${id}`;
connection.query(sql, (err, rows) => { ... });
```
Безопасный вариант на Node.js (параметры):
```js
// безопасно: параметризованный запрос
const id = parseInt(req.query.id, 10);
if (Number.isNaN(id)) {
// обработать ошибку
return;
}
connection.query('SELECT * FROM users WHERE id = ?', [id], (err, rows) => { ... });
```
Пояснение: placeholder (`?`) и массив параметров передают данные отдельно от SQL, предотвращая инъекцию.
Коротко о дополнительных мерах предотвращения:
- Валидировать и фильтровать входные данные (whitelist по формату, длине, типу).
- Минимизировать привилегии учетной записи БД (чтение/запись только там, где нужно).
- Не показывать подробные ошибки SQL пользователю.
- Использовать ORM/абстракции и проверенные библиотеки, но не полагаться лишь на них.
- Для динамических идентификаторов (имена таблиц/полей) — применять строгую белую выборку значений, не конкатенировать напрямую.
Эти принципы вместе предотвращают большинство SQL‑инъекций.