// Безопасность / Веб

XSS: межсайтовый скриптинг — типы, атаки и защита

XSS — межсайтовый скриптинг: типы и защита

XSS (Cross-Site Scripting) — внедрение чужого JavaScript, который выполняется в браузере жертвы в контексте вашего сайта. Через XSS крадут сессии, угоняют аккаунты, ставят скиммеры карт и фишат прямо на легитимном домене. Уязвимости почти 20 лет, но она в OWASP Top 10 2021 (A03) и стабильно в топе выплат bug bounty. И это одна из самых частых наших находок на пентесте веб-приложений.

XSS, которые вошли в историю

  • Червь Samy (MySpace, 2005) — хранимый XSS в профиле добавлял автора в друзья и копировал себя в профиль жертвы. За ~20 часов — больше миллиона «друзей». Классика, показавшая мощь stored XSS.
  • British Airways (2018) — атака Magecart: внедрённый в сайт скрипт-скиммер увёл данные ~380 000 карт. Итог — штраф ICO £20 млн по GDPR. XSS/инъекция скрипта = прямые деньги.
  • TweetDeck (2014) — самораспространяющийся XSS-червь заставил тысячи аккаунтов автоматически ретвитить эксплойт; Twitter пришлось временно отключить сервис.

Три типа XSS — с рабочими пейлоадами

1. Reflected (отражённый)

Скрипт передаётся в запросе и сразу отражается в ответе. Классика — поиск без экранирования:

https://site.com/search?q=">

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

Скрипт сохраняется на сервере (комментарий, имя, отзыв, адрес) и срабатывает у каждого, кто открывает страницу. Ссылка не нужна. Один stored XSS в админ-панели = все админы скомпрометированы. Именно этот тип даёт червей вроде Samy.

3. DOM-based

Уязвимость целиком на клиенте: небезопасная работа JS с данными. Источник (source) → приёмник (sink):

// ❌ данные из URL напрямую в HTML
document.getElementById("out").innerHTML = location.hash.slice(1);
// payload: site.com/#

Актуально для SPA на React/Vue/Angular. И запомните: alert(1) в пейлоадах — это просто маркер «код выполнился». Реальная нагрузка ворует куки и токены.

Что реально делают через XSS

  • Кража сессии: отправка document.cookie (если нет HttpOnly).
  • Кража токенов из localStorage: SPA часто хранят JWT в localStorage — он полностью читается из JS. Аргумент против хранения токенов там.
  • Действия от имени жертвы: скрипт делает запросы с её правами — смена email, создание админа, перевод.
  • Скиммер карт (как British Airways) и фейковые формы логина прямо на настоящем домене.
  • BeEF-хук — полный контроль над браузером жертвы.

Защита: пять слоёв, а не чёрный список

Фильтровать «запрещённые слова» бесполезно — обходится атрибутами, событиями, SVG, кодированием. Работает многослойная защита:

  1. Контекстное экранирование вывода. HTML, атрибут, JS и URL требуют разного экранирования. Используйте API фреймворка, не ручную склейку строк.
  2. Content Security Policy (CSP). Нейтрализует большинство XSS даже при наличии инъекции.
  3. DOMPurify для пользовательского HTML; в React/Vue избегайте dangerouslySetInnerHTML/v-html.
  4. HttpOnly + Secure + SameSite на cookie сессии — тогда сработавший XSS не украдёт сессию.
  5. Валидация ввода как дополнительный барьер.
# строгая CSP, нейтрализующая большинство XSS
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none';
  base-uri 'none'; frame-ancestors 'none'

# безопасная очистка пользовательского HTML
import DOMPurify from "dompurify";
el.innerHTML = DOMPurify.sanitize(userHtml, { ALLOWED_TAGS: ["b","i","a","p","ul","li"] });

Экспертное наблюдение: самые дорогие XSS живут не в блоге, а на «служебных» путях — письма-уведомления, PDF-генераторы, экспорты, админ-логи. Разработчик экранировал вывод в шаблоне сайта, но забыл про рассылку, которую открывает бухгалтер или админ с полными правами. Мы всегда проверяем именно эти неочевидные приёмники.

Как это тестируется

Найти XSS во всех контекстах вывода можно только ручной проверкой каждой точки ввода: формы, параметры URL, заголовки (Referer, User-Agent), загружаемые файлы (имена, метаданные), импортируемые данные. Для SPA отдельно трассируется цепочка source→sink. Делаем это на пентесте веб-приложений и пентесте SPA.

На вашем сайте точно нет XSS?

Проверим все точки ввода и контексты вывода — reflected, stored и DOM-based, включая письма и экспорты — и покажем, как закрыть.

Заказать пентест →

Читайте также: IDOR на практике, Пентест SPA.

← назад в блог