Cross-Site Scripting (XSS)

Cross-Site Scripting (XSS)
Cross-Site Scripting (XSS) — это уязвимость веб-приложения, которая позволяет злоумышленнику выполнить произвольный JavaScript-код в браузере другого пользователя в контексте доверенного сайта.
Обычно XSS возникает, когда приложение отображает динамические данные, не обрабатывая пользовательский ввод как обычный текст. Вместо этого браузер воспринимает эти данные как HTML или JavaScript и выполняет их.
Виды XSS
Существует три основных вида XSS.
DOM-based XSS
DOM-based XSS происходит полностью на стороне клиента. Такая уязвимость возникает, когда JavaScript-код в браузере получает недоверенные данные, например из параметров URL, и вставляет их в страницу с помощью небезопасных API, таких как innerHTML.
Безопасные API, например textContent, создают текстовый узел DOM вместо разбора строки как HTML. Благодаря этому браузер не интерпретирует содержимое как исполняемый код.
Reflected XSS
Reflected XSS возникает, когда сервер сразу включает пользовательский ввод в HTML-ответ.
Типичный пример — страница поиска, которая отображает поисковый запрос без правильного экранирования. Злоумышленник может сформировать специальную ссылку и убедить пользователя открыть её. В результате вредоносный код попадёт в ответ сервера и выполнится в браузере пользователя.
Stored XSS
Stored XSS — наиболее опасный тип, поскольку вредоносный код сохраняется, как правило, в базе данных.
Например, если сайт позволяет оставлять комментарии и отображает их без необходимой защиты, каждый посетитель страницы выполнит JavaScript-код злоумышленника.
В отличие от Reflected XSS и DOM-based XSS, которые обычно требуют, чтобы пользователь открыл специально подготовленную ссылку, Stored XSS затрагивает всех пользователей, посещающих уязвимую страницу.
Что может сделать XSS?
Поскольку вредоносный код выполняется в браузере в контексте сайта, он может:
- Читать данные со страницы.
- Читать cookies, которые не помечены флагом
HttpOnly. - Читать данные из Local Storage и Session Storage.
- Выполнять аутентифицированные запросы от имени пользователя.
- Изменять содержимое страницы.
- Выполнять любые действия, доступные авторизованному пользователю.
Даже если cookies защищены флагом HttpOnly, XSS всё равно может выполнять запросы от имени пользователя, потому что браузер автоматически прикрепляет cookies ко всем запросам к этому сайту.
Защита от XSS
Output Encoding
Output Encoding — основной способ защиты от XSS.
Его идея проста: перед выводом данных необходимо заменить символы, имеющие специальное значение в HTML, на их безопасные представления.
Например:
<превращается в<>превращается в>
После этого браузер воспринимает данные как обычный текст, а не как HTML-разметку. Поэтому новые HTML-элементы или JavaScript-код не создаются.
Способ кодирования зависит от контекста, в который вставляются данные:
- содержимое HTML → HTML Encoding;
- атрибут HTML → Attribute Encoding;
- URL → URL Encoding;
- строка JavaScript → JavaScript (Unicode) Encoding.
Очень важно использовать кодирование, соответствующее месту вывода данных.
Санитизация (Sanitization)
Иногда пользователям необходимо разрешить использовать HTML, например при написании статьи или комментария.
В этом случае нельзя просто закодировать весь текст, иначе HTML будет отображаться как обычный текст.
Вместо этого применяется санитизация. Санитайзер удаляет потенциально опасные конструкции, например:
- элементы
<script>; - обработчики событий (
onclick,onerrorи другие); - опасные URL, например
javascript:.
После удаления опасных элементов оставшийся безопасный HTML можно отобразить пользователю.
Content Security Policy (CSP)
Content Security Policy (CSP) — это дополнительный уровень защиты, который ограничивает, какие ресурсы разрешено загружать странице.
Например, CSP позволяет ограничить загрузку:
- JavaScript;
- CSS;
- изображений;
- шрифтов;
- фреймов.
Кроме этого CSP может:
- запретить выполнение встроенных (inline) скриптов;
- запретить использование
eval()и похожих функций; - разрешить загрузку JavaScript только с доверенных источников.
Если встроенные скрипты всё же необходимы, сервер может генерировать криптографически стойкое случайное значение (nonce) для каждого HTTP-ответа. Это значение передаётся одновременно в заголовке CSP и в разрешённых встроенных тегах <script>. Браузер выполнит только те встроенные скрипты, nonce которых совпадает со значением из политики.
Важно понимать, что CSP является дополнительной защитой (defense in depth). Он уменьшает последствия возможной XSS-уязвимости, но не заменяет правильное кодирование данных и санитизацию.
Современные фреймворки
Большинство современных фреймворков по умолчанию защищают от XSS, автоматически экранируя динамические данные.
Например:
- ASP.NET Razor;
- React;
- Angular;
- Vue.
Однако эту защиту можно обойти, если разработчик намеренно выводит необработанный HTML с помощью API, таких как:
innerHTML;dangerouslySetInnerHTML;Html.Raw().
Использовать подобные API следует только с доверенным или предварительно санитизированным HTML.
Итоги
Cross-Site Scripting остаётся одной из самых распространённых уязвимостей веб-приложений.
Главное правило защиты — считать любые пользовательские данные недоверенными и всегда применять кодирование, соответствующее контексту вывода. Если приложение должно принимать HTML, его необходимо предварительно санитизировать. Дополнительно рекомендуется использовать Content Security Policy, чтобы снизить последствия возможных ошибок в приложении.