Content Security Policy (CSP) является одним из важнейших механизмов, направленных на защиту веб-приложений от различных атак типа Cross-Site Scripting (XSS). Одним из популярных подходов к реализации CSP является использование случайно сгенерированных уникальных nonce, которые позволяют запускать только те скрипты, которые содержат этот nonce в атрибуте script. В теории, nonce должен использоваться только один раз и предотвращать выполнение вредоносных скриптов, не имеющих соответствующей метки. Однако на практике существуют некоторые тонкости, связанные с кэшированием в браузере и особенностями реализации браузерных механизмов, которые могут быть использованы для обхода этой защиты. Одним из таких методов является использование дискового кэша браузера и бэк/форвард кеша (bfcache) для повторного использования старого nonce и запуска вредоносного кода.
Изучение подобных методов важно для повышения шифрования безопасности как для разработчиков веб-сайтов, так и для специалистов по информационной безопасности. Первоначальный вызов: почему nonce и кеш браузера вызывают проблему? Nonce, как следует из названия, должен использоваться лишь единожды для одного ответа сервера. Однако если страница с фиксированным nonce попадёт в кэш браузера, пользователь при повторном открытии страницы получит тот же самый nonce, который может быть уже известен злоумышленнику. Благодаря этому появляется возможность применить уже известный nonce для выполнения вредоносных скриптов, обходя CSP. Кэширование — одна из базовых технологий браузеров, предназначенная для повышения скорости загрузки страниц, но в контексте безопасности оно может создавать неожиданные проблемы.
Кроме того, существуют различные уровни кэширования в браузере: память, диск и особый back/forward cache. Последний хранит «моментальные снимки» страниц, которые позволяют пользователям быстро переключаться между вкладками или использовать кнопки назад и вперёд. В случае провалов условий для сохранения страницы в bfcache браузер откатывается к использованию более простого дискового кэша. Именно на этой особенности основан продвинутый обход CSP с помощью nonce. Методика утечки nonce с помощью CSS Одним из ключевых препятствий на пути эксплуатации nonce CSP является невозможность напрямую получить nonce из атрибута script с помощью большинства средств JavaScript или CSS в браузере из соображений безопасности.
Однако nonce обычно выводится и в других местах страницы, например, в мета-теге с определением CSP через http-equiv. Этот атрибут не защищён так же жёстко и доступен для CSS селекторов. Используя CSS-инъекции и особенности селекторов атрибутов, можно построить стили, которые через фоновые изображения будут делать запросы на сервер при совпадении части значения nonce, содержащейся в meta теге. Получив множество таких фрагментов, сервер сможет реконструировать полный значение nonce, даже несмотря на его случайность и сложность. Это весьма хитрый и тонкий метод, позволяющий злоумышленнику отделить этап получения nonce от этапа внедрения вредоносного кода.
Выделение уязвимостей веб-приложения: CSRF на входе Для того чтобы эффективно внедрять вредоносные воздействия в систему при известном nonce, атакующий должен иметь возможность менять содержимое, вызывающее XSS. В представленном контексте эта задача решается через уязвимость Cross-Site Request Forgery (CSRF) на странице входа, которая позволяет атакующему заставить жертву отправить поддельный POST-запрос с изменёнными пользовательскими данными. Это значительно расширяет возможные сценарии атак. Реализовав CSRF, злоумышленник может менять куки с именем пользователя, которые затем используются в небезопасном .innerHTML на странице dashboard.
Это даёт возможность подставлять свои скрипты даже с учетом nonce, несмотря на CSP. Роль кеша в обходе nonce CSP Ключевой сложностью остаётся то, что при каждом повторном запросе страницы nonce должен быть новым, следовательно, знание старого nonce бесполезно для запуска нового скрипта. Это правда в большинстве случаев, однако браузерные кеши дают возможность обхода. Особенно интересна техника работы с back/forward cache. Если страницу открыть, потом перейти назад, браузер покажет её не с сервера, а из кеша.
При наличии особенностей или ошибок, страница может попадать не в bfcache с сохранением состояния JS, а в дисковый кэш, где тело страницы и ресурсы сохранены, но скрипты и их состояния обновляются. Именно таким образом можно получить страницу с известным старым nonce, а данные пользователя обновить. Все это возможно, если учитывать политические правила браузерного кеширования, особенно новый механизм cache partitioning, который разделяет кэш ресурсов по контекстам и топ-уровню сайтов, усложняющий некоторые методы атаки, но не закрывающий их полностью. Практическая схема атаки Атака начинается с внедрения CSS, который позволяет через селекторы атрибутов headers meta или script запросами к серверу узнать nonce. После этого преступник с помощью CSRF заставляет пользователя залогиниться с поддельным именем, включающим в себя вредоносный скрипт с известным nonce.
Далее активируется механизм обновления страницы и изменения кеша так, чтобы страница с известным nonce была загружена из кеша, а запросы данных (например, /profile) обновлены с новыми вредоносными значениями. Использование переходов назад и вперёд, а также хитроумных переходов через промежуточные URL с разными query параметрами позволяют избежать перезаписи кэша nonce и одновременно обновить полезные данные. В результате, несмотря на строгую CSP, злоумышленнику удаётся выполнить свой скрипт с соответствующим nonce, обходя защиту. Погружение в технологии браузера: понимание механизмов Disk Cache и bfcache Back/Forward Cache (bfcache) — это кэш страницы целиком, включая JavaScript и состояние UI, который активируется при нажатии кнопок назад и вперёд в браузере. Это делает навигацию мгновенной и комфортной.
Однако есть много условий, при которых страница не сохраняется в этой форме, и тогда браузер использует более простой дисковый кэш — сохранённый HTML, CSS, JS и прочие ресурсы без активного состояния. Понимание этих уровней позволяет злоумышленнику контролировать, откуда именно будет загружена страница и ресурсы с какими значениями nonce и данными. Механизм Cache partitioning усложняет ситуацию для атакующего, разделяя кэши ресурсов по топ-уровню сайтов и контексту, что защищает некоторые виды данных от межсайтового доступа. Однако в описанном сценарии за счёт особенностей запросов на ресурсы на одном и том же сайте, но с разными путями, а также точным управлением с помощью query параметров, можно обойти и это. Дополнительные методы усиления атаки и сокращения взаимодействий Чтобы уменьшить количество действий пользователя, злоумышленники могут использовать meta тег refresh на вредоносной странице, который позволяет сразу возвращать пользователя на сайт с уязвимостью, после чего повторять цикл атаки.
Это позволяет ставить атаку на «автопилот», сводя к минимуму обратные действия. Заключение и рекомендации Применение nonce в CSP — мощная практика для предотвращения исполнения нежелательных скриптов, но она далеко не панацея. Когда nonce внедряется в HTML, который может быть закеширован, а сам nonce может быть выявлен с помощью CSS-инъекций и хитрого анализа, появляется серьезная угроза обхода CSP. Поэтому важно при разработке веб-приложений внимательно продумывать методы инвалидации кэширования, используя заголовки, предотвращающие хранение страниц с nonce или, по крайней мере, ограничивающие их доступность. Также применение CSP в заголовках HTTP, а не через мета-теги, повышает уровень защиты.
Не менее важно закрывать потенциальные CSRF уязвимости, поскольку они играют критическую роль в описанных сценариях. Установка защищённых куки, использование токенов, проверка Origin и Referer — стандартные меры противодействия. Изучение подобных техник обхода CSP помогает лучше понять слабые места браузеров и сервисов, а также совершенствовать методы обеспечения безопасности веб-приложений. Этот пример демонстрирует, что безопасность — это не только о политике заголовков, но и о глубоком понимании поведения браузеров и взаимодействия различных технологий.