PHP-безопасность: Как защитить сайт от SQL-инъекций и XSS

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

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

Начать курс

Ваш PHP-код под прицелом: почему молчание — не золото

Представьте: вы запустили сайт, всё работает как часы, клиенты довольны. А в это время где-то в темном углу интернета скрипт-кидди вводит в вашу форму поиска волшебную строку ' OR 1=1 --. И база данных, словно послушный слуга, выдаёт ему все пароли пользователей. Знакомая история? К сожалению, для многих начинающих разработчиков — да.

Безопасность в PHP — это не скучная бюрократия, а реальная защита вашего времени, денег и репутации. Сегодня разберем двух главных врагов веб-приложений: SQL-инъекции и межсайтовый скриптинг (XSS). Вы узнаете, как их распознать, обезвредить и больше никогда не допускать в своем коде.

SQL-инъекции: когда пользователь становится хакером

SQL-инъекция — это атака, при которой злоумышленник «впрыскивает» вредоносный SQL-код через поля ввода. Если вы просто вставляете данные пользователя в запрос, считайте, что вы сами открыли дверь грабителю.

Как выглядит уязвимый код

Вот классический пример, который до сих пор встречается в старых учебниках и на живых сайтах:

<?php
// ОПАСНО! Никогда так не делайте
$username = $_POST['username'];
$password = $_POST['password'];

$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = mysqli_query($conn, $query);
?>

Что произойдёт, если в поле username ввести admin' --? Запрос превратится в:

SELECT * FROM users WHERE username = 'admin' --' AND password = ''

Двойной дефис -- комментирует остаток запроса. Вуаля — злоумышленник заходит как админ без пароля. А если ввести '; DROP TABLE users; --? Прощайте, пользователи.

Спасательный круг: подготовленные запросы (Prepared Statements)

Лучший способ защититься от SQL-инъекций — использовать подготовленные запросы через PDO или MySQLi. Они разделяют логику запроса и данные, поэтому вредоносный код никогда не выполнится.

Вот как выглядит безопасный код с PDO:

<?php
// БЕЗОПАСНО! Используем подготовленные запросы
$username = $_POST['username'];
$password = $_POST['password'];

$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
$stmt->execute(['username' => $username, 'password' => $password]);
$user = $stmt->fetch();
?>

Теперь даже если злоумышленник передаст admin' OR 1=1 --, PDO воспримет это как обычную строку, а не как часть SQL-команды. База данных скажет: «Ищем пользователя с таким странным именем». И не найдёт.

Почему PDO, а не mysql_*?

  • Безопасность по умолчанию: PDO автоматически экранирует данные.
  • Гибкость: поддерживает разные СУБД (MySQL, PostgreSQL, SQLite).
  • Именованные плейсхолдеры: код становится читаемым и поддерживаемым.
  • Объектно-ориентированный подход: современный стиль программирования.

Функции mysql_* удалили из PHP 7.0, и это к лучшему. Если вы всё ещё используете mysqli_real_escape_string — это лучше, чем ничего, но PDO всё равно надёжнее.

XSS-атаки: когда код прячется в комментариях

Межсайтовый скриптинг (XSS) — это внедрение JavaScript-кода на страницу через пользовательский ввод. Представьте: пользователь оставляет комментарий, а вместо текста пишет:

<script>alert('Ваш сайт взломан!');</script>

Если вы выводите это без обработки, каждый посетитель увидит всплывающее окно. А если вместо alert будет код, крадущий куки или перенаправляющий на фишинговый сайт?

Типы XSS-атак

  • Reflected (отражённый): вредоносный код передаётся через URL (например, в параметре поиска).
  • Stored (сохранённый): код сохраняется в базе данных (комментарии, сообщения форума).
  • DOM-based: атака происходит на стороне клиента через манипуляции с DOM.

Как защититься: экранирование вывода

Золотое правило PHP-разработчика: никогда не доверяйте пользовательским данным. Фильтруйте на входе, экранируйте на выходе.

Для вывода данных в HTML используйте htmlspecialchars():

<?php
// Безопасный вывод
$comment = $_POST['comment']; // Допустим, уже прошёл валидацию
echo "<p>" . htmlspecialchars($comment, ENT_QUOTES, 'UTF-8') . "</p>";
?>

Параметры ENT_QUOTES экранирует и одинарные, и двойные кавычки. UTF-8 — обязательный параметр для корректной работы с кириллицей.

Продвинутая защита: Content Security Policy

Даже если вы забыли экранировать один параметр, Content Security Policy (CSP) может спасти ситуацию. Это HTTP-заголовок, который сообщает браузеру, откуда можно загружать скрипты и стили.

Пример настройки CSP в PHP:

<?php
header("Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com;");
?>

Теперь даже если злоумышленник внедрит <script>...</script>, браузер его не выполнит — только скрипты с вашего домена и доверенного CDN.

Практический чек-лист: делаем код неуязвимым

Чтобы не забыть ничего важного, вот пошаговая инструкция для каждого запроса и вывода:

1. Для работы с базой данных

  • Используйте PDO или MySQLi с подготовленными запросами.
  • Никогда не конкатенируйте пользовательские данные в SQL.
  • Настройте режим ошибок PDO на исключения (PDO::ERRMODE_EXCEPTION).

2. Для вывода данных

  • Всегда используйте htmlspecialchars() с параметрами ENT_QUOTES и

Блоги

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