Как санировать пользовательский ввод в PHP: топ ошибок и защиты

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

«А я просто экранирую кавычки»: почему ваш код уязвим

Если вы когда-нибудь гуглили «How can I sanitize user input with PHP?», вы не одиноки. Этот вопрос на Stack Overflow собрал сотни тысяч просмотров. И неудивительно: тема безопасности — одна из самых горячих в веб-разработке.

Но вот парадокс: большинство ответов на тот вопрос либо устарели, либо опасны. Разработчики до сих пор пишут что-то вроде mysql_real_escape_string() (которой уже 10 лет как нет) или просто удаляют «опасные» символы. Спойлер: это не работает.

В этой статье мы разберём топ-5 ошибок санитации пользовательского ввода в PHP, которые превращают ваш сайт в решето, и покажем, как их исправить раз и навсегда.

Ошибка №1: Слепая вера в «санитацию» как универсальную таблетку

Самая большая ошибка — думать, что существует одна функция, которая «очистит» любые данные. Вот типичный код новичка:

<?php
$name = $_POST['name'];
$name = strip_tags($name);
$name = addslashes($name);
// Теперь данные безопасны? Нет!
?>

Проблема в том, что разные контексты требуют разной обработки. Валидация (проверка формата) и санитация (очистка) — это не одно и то же. А ещё есть экранирование вывода, которое вообще происходит в другом месте.

Как надо: Определите, что вы делаете с данными на каждом этапе. Для базы данных — одно, для HTML — другое, для JSON — третье.

Ошибка №2: Использование устаревших функций (или их неправильное применение)

Давайте пройдёмся по «динозаврам» PHP-безопасности:

  • mysql_real_escape_string() — удалена в PHP 7.0. Забудьте.
  • addslashes() — не спасает от всех SQL-инъекций, особенно при многобайтовых кодировках.
  • strip_tags() — удаляет теги, но не экранирует вывод. XSS всё ещё возможен через атрибуты.

Пример опасного кода, который до сих пор встречается:

<?php
$id = $_GET['id'];
$id = addslashes($id);
$query = "SELECT * FROM users WHERE id = '$id'";
// Если id = 1 OR 1=1 -- вас взломали
?>

Как надо: Используйте подготовленные запросы (prepared statements). Это единственный надёжный способ защиты от SQL-инъекций.

<?php
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $_GET['id']]);
?>

Видите? Никакой санитации для SQL. Просто параметры. База данных сама разберётся, что есть данные, а что — команда.

Ошибка №3: Путаница между валидацией и санитацией

Многие думают: «Я удалю все подозрительные символы, и будет безопасно». Но это ломает логику приложения. Представьте, что пользователь ввёл пароль P@ssw0rd!, а вы удалили символ @ и !. Пароль стал Pssw0rd — и пользователь больше не может войти.

Валидация — проверяет, соответствует ли ввод ожидаемому формату (email, число, URL). Если нет — отклоняем.

Санитация — удаляет или изменяет нежелательные символы, но только когда это действительно нужно (например, для имени файла).

Пример правильной валидации email:

<?php
$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
if ($email === false) {
    // Email невалиден — не сохраняем, не выводим
    echo "Введите корректный email";
    exit;
}
// Теперь $email — безопасная строка для любых целей
?>

Золотое правило: Если вы ждёте число — проверьте, что это число. Если email — проверьте формат. Всё остальное — отклоняйте.

Ошибка №4: Игнорирование контекста вывода (XSS)

Вы сохранили данные в базу через подготовленные запросы. Ура, SQL-инъекций нет. Но потом вы выводите эти данные на страницу:

<?php
echo "<p>Привет, " . $user['name'] . "!</p>";
// Если $user['name'] = "<script>alert('XSS')</script>"
?>

Бинго! Вы только что подарили хакеру возможность выполнить JavaScript на вашем сайте. Это называется XSS (межсайтовый скриптинг).

Многие думают, что достаточно htmlspecialchars() для всего. Но это не так:

  • В HTML-контексте (между тегами) — да, htmlspecialchars($data, ENT_QUOTES, 'UTF-8').
  • В атрибутах HTML — тоже htmlspecialchars, но с осторожностью.
  • В JavaScript (внутри <script>) — нужно экранировать для JS.
  • В URL — urlencode().
  • В JSON — json_encode().

Пример правильного вывода:

<?php
// Для HTML
echo "<p>Привет, " . htmlspecialchars($user['name'], ENT_QUOTES, 'UTF-8') . "!</p>";

// Для атрибута
echo '<input value="' . htmlspecialchars($search, ENT_QUOTES, 'UTF-8') . '">';

// Для JavaScript (лучше не использовать, но если надо)
echo "<script>var name = " . json_encode($user['name']) . ";</script>";
?>

Запомните: Экранирование вывода — это не санитация ввода. Это отдельный этап, который происходит в момент отображения данных, а не при их сохранении.

Ошибка №5: Игнорирование глобальных настроек и кодировок

Вы используете htmlspecialchars() с кодировкой по умолчанию? Поздравляю, вы в зоне риска. Если кодировка не указана, PHP использует ISO-8859-1 (или более старую), что может привести к обходу экранирования через многобайтовые символы.

Пример уязвимости:

<?php
// Опасно — кодировка не указана
echo htmlspecialchars($data);

// Безопасно — явно указываем                

Блоги

Рекомендаций по книгам