Avatar ·

Как правильно очищать пользовательский ввод в PHP: защита от XSS и SQL-инъекций

Всем привет! Я начинающий PHP-разработчик и столкнулся с проблемой безопасности. На моём сайте есть форма регистрации и комментариев. Я принимаю данные от пользователей и сразу вставляю их в базу данных и вывожу на страницу. Недавно заметил, что кто-то ввёл в поле имени такой код: ``, и при выводе на страницу этот скрипт выполнился. Также я слышал про SQL-инъекции, но не до конца понимаю, как от них защищаться. Вот пример моего кода: ```php // Получение данных из формы $name = $_POST['name']; $email = $_POST['email']; // Сохранение в БД (MySQL) $query = "INSERT INTO users (name, email) VALUES ('$name', '$email')"; mysqli_query($connection, $query); // Вывод на страницу echo "Привет, $name!"; ``` Как правильно очищать данные? Нужно ли использовать `htmlspecialchars()` или `strip_tags()`? А для базы данных — `mysqli_real_escape_string()`? Или есть более современные подходы? Подскажите, пожалуйста, лучшие практики для PHP 8+. Теги: php, безопасность, xss, sql-injection, user-input <<>>

Проблема в вашем подходе: смешивание контекстов

Вы совершаете классическую ошибку начинающих разработчиков — пытаетесь "очистить" данные один раз для всех случаев. На самом деле, санитизация должна зависеть от контекста использования данных. Данные для SQL-запроса, для вывода в HTML и для хранения в базе требуют разных подходов.

1. Защита от SQL-инъекций (правильная работа с БД)

Использование mysqli_real_escape_string() — это устаревший и ненадёжный метод. Современный и правильный способ — подготовленные запросы (Prepared Statements) через PDO или MySQLi. Они автоматически экранируют данные и предотвращают инъекции.

// Правильный способ с PDO
$pdo = new PDO('mysql:host=localhost;dbname=mydb', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (:name, :email)");
$stmt->execute([
    ':name' => $_POST['name'],
    ':email' => $_POST['email']
]);

// Или с MySQLi
$stmt = $connection->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
$stmt->bind_param("ss", $_POST['name'], $_POST['email']);
$stmt->execute();

2. Защита от XSS (вывод в HTML)

При выводе данных на страницу используйте htmlspecialchars() с правильными параметрами. Эта функция преобразует специальные HTML-символы в их безопасные сущности.

// Правильный вывод на страницу
$safe_name = htmlspecialchars($name, ENT_QUOTES | ENT_HTML5, 'UTF-8');
echo "Привет, $safe_name!";

// Для более сложных случаев (например, если нужно разрешить некоторые теги)
// используйте библиотеку HTML Purifier
require_once 'vendor/autoload.php';
$purifier = new HTMLPurifier();
$safe_html = $purifier->purify($dirty_html);

3. Общие рекомендации по санитизации

  • Фильтрация по типу данных: Используйте filter_var() для проверки email, URL, целых чисел и т.д.
  • Валидация: Проверяйте, что данные соответствуют ожидаемому формату (длина, символы, диапазон).
  • Никогда не доверяйте пользовательскому вводу: Всегда обрабатывайте данные как потенциально опасные.
// Пример комплексной обработки
$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
if ($email === false) {
    die('Некорректный email');
}

$name = trim($_POST['name']);
if (strlen($name) > 100) {
    die('Имя слишком длинное');
}

// Для базы данных — подготовленные запросы
// Для вывода — htmlspecialchars

4. Что не нужно делать

  • Не используйте strip_tags() — это удаляет все HTML-теги, но не защищает от XSS в атрибутах.
  • Не применяйте mysqli_real_escape_string() — это не защищает от всех видов инъекций и сложно в поддержке.
  • Не смешивайте санитизацию с валидацией — это разные задачи.

Итог: Никогда не "очищайте" данные в одном месте для всех случаев. Используйте подготовленные запросы для БД, htmlspecialchars() для вывода в HTML и filter_var() для валидации типов данных. Для сложных случаев (редакторы WYSIWYG) применяйте HTML Purifier.

<<>>

Альтернативный подход: использование современных фреймворков

Если вы разрабатываете не просто учебный проект, а реальное приложение, настоятельно рекомендую использовать один из современных PHP-фреймворков (Laravel, Symfony, Yii2). Они решают проблемы безопасности на уровне архитектуры:

  • Eloquent ORM (Laravel) — автоматически использует подготовленные запросы.
  • Шаблонизаторы (Blade, Twig) — по умолчанию экранируют вывод через {{ }}, что защищает от XSS.
  • Встроенная валидация — правила для проверки email, URL, длины строк и т.д.
  • CSRF-защита — встроенная защита от межсайтовой подделки запросов.
// Пример в Laravel
// Контроллер
public function store(Request $request) {
    $validated = $request->validate([
        'name' => 'required|string|max:100',
        'email' => 'required|email|unique:users',
    ]);
    
    User::create($validated);
    
    // В шаблоне Blade автоматическое экранирование
    // {{ $user->name }} — безопасно
    // {!! $user->bio !!} — неэкранированный вывод (только для доверенного контента)
}

Фреймворки также предоставляют middleware для глобальной обработки ввода, системы логирования атак и другие инструменты безопасности. Это позволяет не думать о каждой мелочи и сосредоточиться на бизнес-логике.

Важно: Даже с фреймворком нужно понимать основы безопасности, чтобы не допустить ошибок в нестандартных ситуациях (например, при работе с файлами или API).