Featured image of post Фундаментальные различия между XSS и CSRF и современные меры защиты

Фундаментальные различия между XSS и CSRF и современные меры защиты

Как злоумышленники эксплуатируют две главные уязвимости веб-приложений.

Введение

В современных веб-приложениях безопасность — это не просто дополнительная функция, а один из самых важных элементов, составляющих основу системы. Среди них XSS (Межсайтовый скриптинг - Cross-Site Scripting) и CSRF (Подделка межсайтовых запросов - Cross-Site Request Forgery) — серьезные уязвимости с давней историей, которые до сих пор обнаруживаются во многих веб-приложениях. Их часто путают, но механизмы атак и меры защиты от них принципиально отличаются.

В этой статье мы раскроем фундаментальные различия между XSS и CSRF, подробно рассмотрим, как злоумышленники эксплуатируют эти уязвимости, и какие современные меры защиты должны внедрять разработчики, с учетом исторического контекста.


1. Глубокое погружение в XSS (Cross-Site Scripting)

XSS — это метод атаки, при котором злоумышленник внедряет вредоносный скрипт (в основном JavaScript) на веб-страницу, заставляя этот скрипт выполняться в браузерах других пользователей, просматривающих эту страницу. Суть этой атаки заключается в том, что «ненадежные данные интерпретируются как исполняемый код без надлежащей обработки».

3 основных типа XSS

XSS можно описывать по способу хранения или отражения данных и по месту их небезопасной интерпретации. Эти категории не исключают друг друга: хранимые или отражённые данные могут также небезопасно обрабатываться на стороне клиента.

1. Stored XSS (Хранимый XSS)

Хранимый XSS может затронуть многих посетителей. Вредоносный скрипт сохраняется на сервере, например в базе данных или файловой системе. Когда пользователь открывает страницу с этими данными, скрипт передаётся браузеру и выполняется. Реальная опасность зависит от привилегий и контекста выполнения.

  • Типичные места возникновения: Разделы комментариев, доски объявлений, профили пользователей, функции отзывов и т. д.
  • Угроза: Масштаб воздействия очень широк, и потенциальными жертвами могут стать все пользователи, открывшие страницу.

2. Reflected XSS (Отраженный XSS)

Reflected XSS возникает, когда вредоносный скрипт отправляется как часть запроса (например, параметры URL или данные формы) без сохранения на сервере, и «отражается» обратно в ответе от сервера в неизменном виде.

  • Типичные места возникновения: Страницы результатов поиска, отображение сообщений об ошибках, передача данных между шагами и т. д.
  • Метод атаки: Злоумышленник осуществляет атаку, заставляя пользователя кликнуть по URL, содержащему вредоносные параметры (используя фишинговые электронные письма или социальные сети).

3. DOM-based XSS

DOM-based XSS возникает, когда клиентский JavaScript обрабатывает недоверенные данные через небезопасный приёмник (sink). Данные могут также проходить через сервер; определяющий признак — небезопасная обработка на стороне клиента.

  • Механизм: Возникает, когда JavaScript приложения считывает данные из источников, контролируемых злоумышленником, таких как window.location или document.referrer, и передает их напрямую в опасные приёмники (sinks), такие как innerHTML или eval().
  • Угроза: Часто не оставляет следов в журналах сервера и может быть трудно обнаружить с помощью WAF (Web Application Firewall) и т. п.

Ущерб от XSS и методы выполнения скриптов в контексте

В случае успешного XSS скрипт злоумышленника выполняется в браузере пользователя с тем же источником (привилегиями), что и сам веб-сайт. Это приводит к следующим серьезным последствиям:

  1. Перехват сеанса: Если у аутентификационного cookie нет HttpOnly, злоумышленник может украсть идентификатор сеанса через document.cookie. Даже при наличии HttpOnly XSS способен отправлять аутентифицированные запросы от имени пользователя.
  2. Выполнение несанкционированных операций: Выполнение произвольных операций в приложении (изменение пароля, денежный перевод, отправка сообщений и т. д.) в фоновом режиме от имени пользователя.
  3. Фишинг: Отрисовка поддельной формы входа в DOM и прямая кража учетных данных пользователя.
  4. Распространение вредоносного ПО: Перенаправление браузера пользователя на набор эксплойтов, заражая компьютер вредоносным ПО.

Современные меры защиты от XSS

Для предотвращения XSS необходим подход глубокоэшелонированной защиты (Defense in Depth).

1. Экранирование в зависимости от контекста (Output Encoding)

Самой базовой и важной мерой является экранирование (кодирование) ввода пользователя, преобразующее его в безопасные строки перед выводом на веб-страницу. Важно выбрать правильный метод экранирования в зависимости от контекста вывода данных (тело HTML, атрибуты HTML, внутри JavaScript, внутри CSS, внутри URL и т. д.). Многие современные веб-фреймворки (React, Vue, Angular и т. д.) выполняют HTML-экранирование по умолчанию, но осторожность по-прежнему необходима.

2. Внедрение CSP (Content Security Policy)

CSP — это очень мощный механизм защиты от XSS, определяющий в HTTP-заголовке белый список ресурсов, которые браузеру разрешено загружать и выполнять.

1
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com;

Таким образом, даже если злоумышленнику удастся внедрить встроенный скрипт <script>alert(1)</script>, его выполнение будет заблокировано CSP.

Добавление атрибута HttpOnly к файлам cookie, в которых хранятся идентификаторы сеансов, предотвращает доступ к ним из JavaScript (например, document.cookie). Хотя это не предотвращает само возникновение XSS, это важная мера смягчения последствий, значительно снижающая риск перехвата сеанса через XSS.


2. Суть CSRF (Cross-Site Request Forgery)

CSRF — это атака, при которой злоумышленник заманивает пользователя на поддельный сайт и заставляет его отправить непреднамеренный запрос к другому веб-сайту, на котором пользователь уже аутентифицирован (вошел в систему).

Главное отличие заключается в том, что в то время как XSS заставляет “выполняться вредоносный скрипт внутри браузера”, CSRF злоупотребляет “стандартным поведением браузера (автоматической отправкой файлов cookie) для отправки несанкционированного запроса”.

Браузер автоматически прикрепляет cookie, когда выполнены условия их отправки. Это может относиться и к запросам, вызванным изображениями или формами с другого сайта. Но SameSite, Secure, Domain, Path и ограничения браузера могут препятствовать отправке.

Сценарий атаки:

Этот учебный пример предполагает уязвимое приложение: аутентификационный cookie допускает межсайтовую отправку с SameSite=None; Secure, а проверки CSRF-токена и Origin отсутствуют. bank.example и attacker.invalid — зарезервированные домены разных сайтов.

  1. Пользователь входит на сайт банка (bank.example) и получает сеансовый файл cookie.
  2. Пользователь в другой вкладке просматривает сайт-ловушку злоумышленника (attacker.invalid).
  3. На сайте-ловушке размещена скрытая форма и скрипт для автоматической отправки, как показано ниже.
    1
    2
    3
    4
    5
    
    <form action="https://bank.example/transfer" method="POST" id="csrf-form">
        <input type="hidden" name="toAccount" value="ATTACKER_ACCOUNT">
        <input type="hidden" name="amount" value="1000000">
    </form>
    <script>document.getElementById('csrf-form').submit();</script>
    
  4. Браузер отправляет POST-запрос на bank.example. В этот момент автоматически прикрепляется сеансовый файл cookie сайта банка.
  5. Поскольку запрос содержит действительный сеансовый файл cookie, сервер банка обрабатывает его как запрос от законного пользователя, и выполняется несанкционированный перевод средств.

Историческое развитие мер защиты от CSRF и современные практики

Чтобы предотвратить CSRF, необходимо проверить, “был ли запрос отправлен с предполагаемой, законной страницы”.

1. CSRF-токены (Anti-CSRF Tokens): Традиционная и надежная защита

Самая старая и широко используемая надежная мера защиты — это CSRF-токены (Паттерн токена синхронизации - Synchronizer Token Pattern).

  • Сервер генерирует непредсказуемый случайный токен для каждой сессии и сохраняет его на стороне сервера (например, в сессии).
  • Токен внедряется как скрытое поле в HTML-форму, отправляемую клиенту.
  • При отправке формы сервер сравнивает полученный токен с токеном, сохраненным на сервере, и обрабатывает запрос только в случае их совпадения. Хотя злоумышленник может заставить отправить запрос с сайта-ловушки, он не может прочитать страницу целевого сайта для получения правильного токена (из-за политики одного источника - Same-Origin Policy), поэтому атака не удается.

Этот метод часто используется в API, которые не сохраняют состояние (сессии) на стороне сервера.

  • Сервер генерирует случайный токен и отправляет его клиенту в виде файла cookie.
  • JavaScript на клиенте считывает значение этого файла cookie и устанавливает его в заголовок запроса (например, X-CSRF-Token) перед отправкой.
  • Сервер проверяет, совпадает ли значение токена в файле cookie со значением токена в заголовке. Выше описана простая проверка равенства. Полагаться только на неё не рекомендуется, если злоумышленник может внедрять cookie. Предпочтительна встроенная защита фреймворка. При использовании Double Submit проверяйте Signed Double Submit-токен, аутентифицированный с помощью HMAC с серверным секретом и привязанный к текущему аутентифицированному сеансу. API с пользовательскими заголовками также не должны разрешать CORS-доступ с учётными данными для недоверенных источников.

Атрибут SameSite управляет межсайтовой отправкой cookie и служит одним из уровней защиты. В приложениях общего назначения сочетайте его с CSRF-токенами или проверкой Origin, а не заменяйте их им.

  • SameSite=Strict: Файлы cookie не отправляются ни при каких межсайтовых запросах, включая навигацию верхнего уровня, такую ​​как клики по ссылкам. Это наиболее безопасно, но может повлиять на UX, например, не сохраняя статус входа при переходе по ссылке с другого сайта.
  • SameSite=Lax: Файлы cookie не отправляются при межсайтовых запросах, таких как загрузка изображений или POST-запросы, но отправляются при навигации верхнего уровня путем клика по ссылке (GET-запрос). В настоящее время это поведение по умолчанию во многих браузерах. Это может предотвратить большинство атак CSRF через отправку вредоносных POST-форм.
  • SameSite=None: Снимает ограничение SameSite на межсайтовую отправку и требует Secure. Другие условия отправки cookie и ограничения браузера продолжают действовать.

SameSite различает сайты, а не источники (origins): соседние поддомены одного регистрируемого домена могут считаться одним сайтом. Lax также не защищает обработчик, меняющий состояние через GET. Используйте подходящие HTTP-методы для изменений состояния вместе с серверной CSRF-проверкой. У стандартного поведения Lax возможны исключения, поэтому необходимые атрибуты cookie следует задавать явно.


Взаимосвязь между XSS и CSRF и заключение

На диаграмме ниже показана разница в ходе атак.

  graph TD
    subgraph XSS Attack
        A["Злоумышленник"] -- "Внедрение вредоносного скрипта" --> B["Веб-сервер"]
        B -- "Страница со скриптом" --> C["Браузер жертвы"]
        C -- "Кража ID сеанса (без HttpOnly)" --> A
    end

    subgraph CSRF Attack
        D["Жертва"] -- "Вход" --> E["Веб-сервер"]
        D -- "Просмотр" --> F["Сайт-ловушка злоумышленника"]
        F -- "Принудительный запрос (с Cookie)" --> E
    end

Хотя XSS и CSRF являются разными уязвимостями, если существует XSS, большинство мер защиты от CSRF становятся недействительными. Это связано с тем, что скрипт, выполняемый через XSS, работает на законной странице и, следовательно, может считывать CSRF-токены и отправлять запросы из того же источника.

Поэтому для обеспечения безопасности веб-приложений требуется построить прочный фундамент, сначала полностью изолировав XSS (путем надлежащего экранирования и CSP), а затем внедрив меры защиты от CSRF (SameSite Cookie и CSRF-токены).

Разработчикам важно не полагаться чрезмерно на функции безопасности, предоставляемые фреймворками, понимать фундаментальные механизмы этих уязвимостей и проектировать защиту на соответствующих уровнях.

Источники

comments powered by Disqus