Введение: почему ваш сайт на PHP может быть взломан прямо сейчас?
Представьте ситуацию: вы написали крутой сайт на PHP, форму обратной связи, комментарии, личный кабинет. Всё работает. Но однажды вы заходите в админку, а там — пустота. Или ещё хуже: на сайте появилась реклама казино, а Google заблокировал ваш ресурс. Знакомо? Корень зла — неправильная обработка пользовательского ввода.
Вопрос "How can I sanitize user input with PHP?" — один из самых популярных на Stack Overflow. Тысячи разработчиков спотыкаются на одних и тех же граблях. В этой статье мы разберём топ ошибок, которые совершают даже опытные программисты, и покажем, как их избежать. Вы узнаете, как защититься от XSS, SQL-инъекций и других атак. Поехали!
Ошибка №1: Использование mysql_real_escape_string() для всего подряд
Старая школа: многие разработчики до сих пор думают, что если обернуть все данные в mysql_real_escape_string(), то проблема решена. Это грубая ошибка. Эта функция предназначена только для экранирования строк в SQL-запросах, и то — для устаревшего расширения mysql_*, которое удалили ещё в PHP 7.
Пример неправильного кода:
<?php
$name = mysql_real_escape_string($_POST['name']);
$query = "INSERT INTO users (name) VALUES ('$name')";
$result = mysql_query($query);
?>
Почему это плохо:
- Функция не защищает от XSS-атак (например, вставки JavaScript).
- Расширение
mysql_*устарело и небезопасно. - Вы смешиваете логику экранирования с бизнес-логикой.
Как исправить: используйте подготовленные запросы (prepared statements) через PDO или MySQLi. А для вывода данных на экран — отдельную санитацию.
Ошибка №2: Игнорирование XSS-атак при выводе данных
Самая популярная ошибка. Вы приняли данные, сохранили в БД и просто выводите их через echo или print. Вуаля — злоумышленник может вставить скрипт, который украдёт куки, перенаправит пользователя или испортит вёрстку.
Пример уязвимого кода:
<?php
echo "<p>Привет, " . $_GET['name'] . "!</p>";
?>
Если в URL передать ?name=<script>alert('XSS')</script>, то скрипт выполнится.
Решение: используйте функцию htmlspecialchars() с параметром ENT_QUOTES:
<?php
$name = htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');
echo "<p>Привет, " . $name . "!</p>";
?>
Важно: санитацию нужно делать именно в момент вывода, а не при сохранении. Если вы замените символы в БД, то потеряете оригинальные данные.
Ошибка №3: Слепая фильтрация через strip_tags()
Многие думают: "О, просто удалю все HTML-теги — и порядок". Но strip_tags() не панацея. Во-первых, она удаляет только теги, но оставляет атрибуты (например, onclick). Во-вторых, если вам нужно разрешить часть тегов (например, <b> или <i>), то strip_tags() становится неудобным.
Пример:
<?php
$comment = strip_tags($_POST['comment']);
echo $comment; // Удалит <script>, но оставит <a onclick="...">
?>
Решение: используйте htmlspecialchars() для обычного текста или библиотеки типа HTML Purifier, если нужно разрешить определённые теги. HTML Purifier проверяет не только теги, но и атрибуты, стили и даже CSS.
Ошибка №4: Отсутствие валидации email, URL и чисел
Санитация — это не только про XSS. Если вы ожидаете email, а получаете строку "123", это может вызвать ошибки. Или если вы сохраняете URL без проверки, злоумышленник может вставить javascript:alert(1).
Пример плохого кода:
<?php
$email = $_POST['email']; // Никакой проверки
$url = $_POST['website']; // Пользователь ввёл "javascript:void(0)"
?>
Как правильно: используйте встроенные функции PHP для валидации:
<?php
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
if ($email === false) {
// Ошибка — невалидный email
}
$url = filter_input(INPUT_POST, 'website', FILTER_VALIDATE_URL);
if ($url === false) {
// Ошибка — невалидный URL
}
$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 120]]);
?>
Функция filter_input() не только проверяет формат, но и санирует данные, удаляя лишние символы.
Ошибка №5: Использование addslashes() вместо prepared statements
В 2024 году писать запросы с addslashes() — это моветон. Эта функция экранирует только одинарные кавычки, но не защищает от других видов атак, например, от обхода через многобайтовые кодировки.
Пример:
<?php
$name = addslashes($_POST['name']);
$query = "SELECT * FROM users WHERE name = '$name'";
// Если передать %bf%27, то экранирование сломается
?>
Решение: используйте PDO или MySQLi с подготовленными запросами. Это надёжно и просто:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->prepare('INSERT INTO users (name, email) VALUES (:name, :email)');
$stmt->execute([':name' => $_POST['name'], ':email' => $_POST['email']]);
?>
Подготовленные запросы автоматически экранируют данные, и вы не сможете случайно сделать инъекцию.
Ошибка №6: Санитация данных в одном месте, а вывод — в другом
Часто новички делают так: при получении данных они применяют htmlspecialchars(), сохраняют в БД, а потом выводят "как есть". Это приводит к двой