← Назад к вопросам

Что такое 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)