Настройка защиты от XSS (Cross-Site Scripting) на сайте
XSS — класс атак, при котором злоумышленник внедряет вредоносный JavaScript в страницы, которые видят другие пользователи. Последствия: кража сессий и cookies, перехват форм ввода, дефейс страниц, редирект на фишинговые сайты.
Три вектора XSS
Reflected XSS — вредоносная нагрузка передаётся через URL-параметры и немедленно отображается на странице. Пример: https://example.com/search?q=<script>alert(document.cookie)</script>.
Stored XSS — нагрузка сохраняется в базе данных (комментарии, профиль пользователя) и выполняется у каждого, кто просматривает страницу.
DOM-based XSS — нагрузка обрабатывается JavaScript на клиентской стороне без участия сервера. Опасен тем, что серверные фильтры его не видят.
Экранирование вывода
Основной инструмент защиты — контекстное экранирование при выводе данных.
PHP/Blade (Laravel):
{{ $userInput }} {{-- автоматически экранирует: & < > " ' --}}
{!! $trustedHtml !!} {{-- RAW вывод — только для доверенного контента --}}
React экранирует JSX-значения по умолчанию:
// Безопасно
<div>{userInput}</div>
// Опасно — использовать только с санированным контентом
<div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} />
Vue:
<!-- Безопасно -->
<span>{{ userInput }}</span>
<!-- Опасно -->
<span v-html="userInput"></span>
Санирование HTML-контента
Если пользователи могут вводить форматированный текст (WYSIWYG-редакторы), нужна библиотека санирования:
// DOMPurify — стандарт для браузерной санации
import DOMPurify from 'dompurify';
const cleanHtml = DOMPurify.sanitize(userInput, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'ul', 'li'],
ALLOWED_ATTR: ['href', 'target'],
ALLOW_DATA_ATTR: false,
});
На сервере (PHP):
// HTMLPurifier
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.Allowed', 'b,i,em,strong,a[href|title],p,ul,li');
$purifier = new HTMLPurifier($config);
$clean = $purifier->purify($userInput);
Заголовки безопасности
Content-Security-Policy — главная защита от XSS на уровне браузера (см. отдельный раздел по CSP).
X-XSS-Protection — устаревший заголовок, современные браузеры его игнорируют. CSP заменяет его полностью.
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
Cookie с флагами безопасности
Даже при успешном XSS атакующий не получит сессионный cookie, если он защищён:
// PHP
setcookie('session', $value, [
'httponly' => true, // недоступен через document.cookie
'secure' => true, // только по HTTPS
'samesite' => 'Strict' // не отправляется при cross-site запросах
]);
# Nginx — добавить флаги к Set-Cookie
proxy_cookie_path / "/; HttpOnly; Secure; SameSite=Strict";
Валидация на входе
Не полагайтесь только на экранирование выхода — валидируйте данные на входе:
// Laravel Validator
$validated = $request->validate([
'name' => 'required|string|max:255|regex:/^[a-zA-Zа-яёА-ЯЁ\s\-]+$/u',
'website' => 'nullable|url',
'comment' => 'required|string|max:5000',
]);
DOM-based XSS: опасные паттерны
// Опасно
document.getElementById('output').innerHTML = location.hash.slice(1);
eval(userData);
setTimeout(userCallback, 1000); // если userCallback — строка
// Безопасно
document.getElementById('output').textContent = location.hash.slice(1);
Особого внимания требуют: innerHTML, outerHTML, document.write, eval, Function(), setTimeout/setInterval со строковыми аргументами.
Тестирование
Инструменты для проверки:
- OWASP ZAP — автоматический сканер
- Burp Suite Community — ручное тестирование
- DOM XSS Scanner — расширение браузера
- В браузере DevTools: вкладка Security, проверка CSP-заголовков
Срок реализации
- Аудит существующего кода на XSS-уязвимости: 1–3 дня
- Исправление найденных проблем: 2–5 дней (зависит от объёма)
- Настройка CSP + инструментарий: 3–5 дней







