XSS: межсайтовый скриптинг и экранирование вывода

XSS возникает, когда данные пользователя попадают на страницу как код, а не как текст.

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

Суть проблемы

Заметьте параллель с SQL-инъекцией: там ввод смешивался с командой к базе, здесь — с разметкой страницы. Если приложение берёт, например, комментарий пользователя и вставляет его в HTML без обработки, то текст вроде «обычный отзыв» отобразится нормально, а текст, содержащий теги, браузер воспримет как настоящую разметку и выполнит вложенный скрипт.

Опасность в том, что такой скрипт выполняется в браузере другого пользователя, от его имени: может украсть его cookie сессии, подменить страницу, выполнить действия за него.

Типы XSS

ТипКак попадает
Хранимый (stored)вредоносный ввод сохраняется в базе и показывается всем (комментарий, профиль)
Отражённый (reflected)приходит в параметре ссылки и сразу возвращается в ответе
DOM-basedскрипт на странице сам небезопасно вставляет данные в DOM

Главная защита: экранирование вывода

Ключевая идея — экранировать вывод: при вставке данных в HTML спецсимволы заменяют на безопасные сущности, чтобы браузер показал их как текст, а не разметку. Символ < превращается в &lt;, > — в &gt; и так далее. Тогда введённые теги отобразятся буквами, а не выполнятся.

Запустите пример простого экранирования (модуль html входит в стандартную библиотеку):

import html

user_input = "<b>Привет</b> и кавычка \" вот"
safe = html.escape(user_input)

print("Как ввёл пользователь:", user_input)
print("После экранирования  :", safe)

Вывод:

Как ввёл пользователь: <b>Привет</b> и кавычка " вот
После экранирования  : &lt;b&gt;Привет&lt;/b&gt; и кавычка &quot; вот

После экранирования угловые скобки превратились в &lt; и &gt; — браузер покажет их как текст, а не как теги, и никакой скрипт не выполнится.

Контекст имеет значение

Экранировать нужно под контекст, куда вставляются данные: внутри HTML-текста — одни правила, внутри атрибута — другие, внутри URL или JavaScript — третьи. К счастью, современные шаблонизаторы (в шаблонах фреймворков) экранируют вывод по умолчанию. Опасность появляется, когда разработчик намеренно отключает экранирование («вставить как сырой HTML») для непроверенных данных.

Дополнительные слои

  • Content Security Policy (CSP). Заголовок, который говорит браузеру, откуда разрешено грузить и исполнять скрипты. Даже при пропущенном XSS он может не дать скрипту выполниться.
  • HttpOnly на cookie сессии. Тогда даже выполнившийся скрипт не прочитает cookie и не украдёт сессию.
  • Валидация ввода. Полезна, но главная защита именно на выводе: один и тот же ввод безопасен в тексте и опасен в HTML.

Итог

  • XSS — это ввод, который браузер исполняет как код страницы.
  • Главная защита — экранирование вывода под нужный контекст.
  • Шаблонизаторы экранируют по умолчанию; опасно отключать это для непроверенных данных.
  • CSP и HttpOnly-cookie — дополнительные слои на случай промаха.
Проверьте себя
1. Когда возникает XSS-уязвимость?
AКогда база данных медленно отвечает
BКогда данные пользователя выводятся на страницу как код, а не как текст
CКогда пароль слишком короткий
DКогда сервер использует HTTPS
2. Что является главной защитой от XSS?
AШифрование базы данных
BЭкранирование вывода под контекст вставки
CУвеличение длины паролей
DОтключение JavaScript на сервере
3. Чем помогает флаг HttpOnly на cookie против последствий XSS?
AЗапрещает XSS полностью
BНе даёт выполнившемуся скрипту прочитать cookie сессии
CШифрует страницу
DУскоряет загрузку
4. Почему говорят, что один и тот же ввод бывает и безопасным, и опасным?
AЭто зависит от длины ввода
BБезопасность зависит от контекста вывода: как текст он безопасен, как HTML — нет
CЭто зависит от времени суток
DВвод всегда одинаково опасен
Поддержать проект