← Назад к вопросам
Что такое SQL-инъекция?
1.0 Junior🔥 301 комментариев
#Базы данных и SQL#Безопасность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
SQL-инъекция (SQL Injection)
SQL-инъекция — это уязвимость безопасности, когда атакующий вставляет вредоносный SQL код в параметры приложения, чтобы изменить структуру или логику SQL запроса. Это одна из самых опасных уязвимостей.
Как работает SQL-инъекция
// Уязвимый код (НИКОГДА ТАК НЕ ДЕЛАЙ!)
$email = $_GET['email'];
$query = "SELECT * FROM users WHERE email = '" . $email . "'";
$result = $pdo->query($query);
// Если пользователь введёт:
// email: admin@example.com' OR '1'='1
// Запрос станет:
// SELECT * FROM users WHERE email = 'admin@example.com' OR '1'='1'
// Это вернёт ВСЕ пользователей! Обход безопасности.
// Ещё хуже:
// email: admin@example.com'; DROP TABLE users; --
// Запрос:
// SELECT * FROM users WHERE email = 'admin@example.com'; DROP TABLE users; --'
// Таблица users будет удалена!
Типы SQL-инъекций
1. Union-based injection
// Запрос: SELECT id, name FROM users WHERE id = 1
// Инъекция: 1 UNION SELECT user, password FROM admin
// Результат: пользователь получит пароли администраторов
2. Boolean-based blind injection
// Запрос: SELECT * FROM users WHERE id = 1 AND '1'='1'
// Инъекция: 1 AND (SELECT COUNT(*) FROM users) > 0
// Атакующий угадывает ответ по времени ответа
3. Time-based blind injection
// Запрос с SLEEP для определения истины/ложи
// id = 1 AND IF(1=1, SLEEP(5), 0)
// Если ответ идёт 5 секунд — условие истинно
4. Error-based injection
// Запрос: SELECT * FROM users WHERE id = extractvalue(0x0a,concat(0x7e,(SELECT password FROM users LIMIT 1)))
// Ошибка БД выводит данные
Как защищаться
1. Prepared Statements (САМЫЙ ЛУЧШИЙ СПОСОБ)
// Защищено
$email = $_GET['email'];
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$email]);
$result = $stmt->fetch();
// SQL шаблон отделён от данных
// БД знает что это данные, а не команда
// Или с named parameters
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => $email]);
2. Использовать ORM (вроде Eloquent)
// Автоматически использует prepared statements
$user = User::where('email', $email)->first();
// Или
$users = User::where('age', '>', $age)->get();
// За кулисами: SELECT * FROM users WHERE age > ? с параметром $age
3. Валидация и санитизация
// Проверяй входные данные
$email = filter_var($_GET['email'], FILTER_VALIDATE_EMAIL);
if (!$email) {
die('Invalid email');
}
// Для чисел
$id = (int) $_GET['id']; // Преобразование в число
// Для массивов
$ids = array_map('intval', explode(',', $_GET['ids']));
// Теперь все значения — числа
4. Принцип наименьших привилегий
// Создай отдельного пользователя БД для приложения
CREATE USER 'app'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT, INSERT, UPDATE ON mydb.* TO 'app'@'localhost';
// Этот пользователь не может DROP TABLE
// Даже если будет инъекция, урон ограничен
5. Логирование и мониторинг
// Логируй подозрительные запросы
if (strpos($query, 'UNION') !== false ||
strpos($query, 'DROP') !== false ||
strpos($query, 'DELETE') !== false) {
log_security_alert('Possible SQL injection attempt: ' . $query);
}
Реальные примеры
Уязвимый код:
$username = $_POST['username'];
$password = $_POST['password'];
$query = "SELECT * FROM users WHERE username = '" . $username . "' AND password = '" . md5($password) . "'";
$result = mysql_query($query); // Устаревшая функция!
// Атакующий вводит:
// username: admin' --
// password: anything
// Запрос: SELECT * FROM users WHERE username = 'admin' -- AND password = ...
// Комментарий (--) игнорирует проверку пароля
// Атакующий залогинился как admin без пароля!
Защищённый код:
$username = $_POST['username'];
$password = $_POST['password'];
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = ? AND password = ?');
$stmt->execute([$username, md5($password)]);
$user = $stmt->fetch();
if ($user) {
$_SESSION['user_id'] = $user['id'];
}
Признаки SQL-инъекции в коде
// ОПАСНО!
$query = "SELECT * FROM users WHERE id = " . $_GET['id'];
DB::raw($query);
DB::query("SELECT * FROM table WHERE column = '" . $input . "'");
DB::select(DB::raw("SELECT * FROM users WHERE id = " . $id));
// Хорошо
User::where('id', $id)->first();
DB::table('users')->where('email', $email)->first();
$pdo->prepare('SELECT * FROM users WHERE id = ?')->execute([$id]);
Инструменты для поиска
# SonarQube найдёт SQL injection уязвимости
sonar-scanner
# PHPStan
phpstan analyse src/
# Security-checker
composer require --dev symfonysecurity-advisories
Вывод
- Никогда не конкатенируй параметры в SQL
- Всегда используй prepared statements
- Предпочитай ORM вроде Eloquent
- Проверяй входные данные
- Логируй подозрительную активность
- Тестируй на уязвимости (OWASP Top 10)