Как защититься от SQL-инъекций?php-18

SQL-инъекции — один из самых опасных видов атак, когда злоумышленник может выполнить произвольный SQL-код через уязвимые параметры приложения. Вот профессиональные методы защиты.

1. Использование подготовленных выражений

PDO (PHP Data Objects):

$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => $userInput]);
$results = $stmt->fetchAll();

MySQLi:

$mysqli = new mysqli('localhost', 'user', 'pass', 'test');
$stmt = $mysqli->prepare('SELECT * FROM users WHERE id = ?');
$stmt->bind_param('i', $userInput); // 'i' для integer
$stmt->execute();
$result = $stmt->get_result();

2. Использование ORM/Query Builder

Пример с Laravel Eloquent:

User::where('email', $request->email)->first();

Пример с Doctrine DBAL:

$queryBuilder = $connection->createQueryBuilder();
$queryBuilder
    ->select('*')
    ->from('users')
    ->where('email = ?')
    ->setParameter(0, $email);

3. Валидация и фильтрация входных данных

Фильтрация:

$cleanId = filter_var($inputId, FILTER_VALIDATE_INT);
if ($cleanId === false) {
    throw new InvalidArgumentException('Invalid ID');
}

Валидация email:

if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    die('Invalid email format');
}

4. Принцип минимальных привилегий

Настройте пользователя БД с ограниченными правами:

  • Только необходимые таблицы
  • Только SELECT/INSERT/UPDATE/DELETE (без DROP, ALTER и т.д.)
  • Отдельный пользователь для админ-панели

5. Экранирование специальных символов

Для MySQLi (менее предпочтительный способ):

$safeInput = $mysqli->real_escape_string($userInput);
$query = "SELECT * FROM users WHERE name = '$safeInput'";

6. Хранимые процедуры

Пример вызова:

$stmt = $pdo->prepare("CALL GetUserByEmail(?)");
$stmt->execute([$email]);

7. Дополнительные меры защиты

  1. Web Application Firewall (WAF) — мод_security для Apache, NAXSI для Nginx
  2. Регулярные обновления СУБД и PHP
  3. Логирование подозрительных запросов
  4. Ограничение длины входных данных

Пример комплексной защиты

try {
    // Валидация
    $email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
    if (!$email) {
        throw new InvalidArgumentException('Invalid email');
    }

    // Подготовленное выражение
    $stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? LIMIT 1');
    $stmt->execute([$email]);
    $user = $stmt->fetch();

    if (!$user) {
        // Логирование попытки входа
        logFailedAttempt($_SERVER['REMOTE_ADDR'], $email);
    }
} catch (PDOException $e) {
    error_log($e->getMessage());
    showGenericError();
}

Опасные антипаттерны

  1. Конкатенация строк запроса:

    $query = "SELECT * FROM users WHERE id = " . $_GET['id']; // ОПАСНО!
    
  2. Использование устаревших функций:

    mysql_query("SELECT * FROM users WHERE name = '".mysql_real_escape_string($name)."'"); // Ненадежно
    
  3. Динамические имена таблиц/колонок:

    $query = "SELECT * FROM " . $_GET['table']; // Очень опасно
    // Решение: белый список допустимых значений
    $allowedTables = ['users', 'products'];
    if (!in_array($_GET['table'], $allowedTables)) {
        die('Invalid table');
    }
    

Резюмируем:

лучшая защита от SQL-инъекций — использование подготовленных выражений (PDO/MySQLi) или ORM. Дополнительно применяйте валидацию входных данных, принцип минимальных привилегий и другие защитные меры. Никогда не доверяйте пользовательскому вводу и не используйте конкатенацию для построения SQL-запросов.