Client-Side Fundamental
  • Добро пожаловать
  • Глава 1 - Начало работы с XSS
    • Браузерная модель безопасности
    • Знакомимся с уязвимостью XSS
    • Более глубокое понимание XSS
    • Опасный псевдопротокол javascript
  • Глава 2 - Защита и Обход для XSS
    • Первая линия обороны от XSS - Sanitization
    • Вторая линия обороны от XSS - CSP (Content Security Policy)
    • Третья линия обороны против XSS - сокращение области воздействия
    • Последние методы защиты от XSS - Trusted Types и встроенный Sanitizer API
    • Обход защитных мер - Обычные способы обхода CSP
    • Обход защитных мер - Mutation XSS
    • Самая опасная XSS - Universal XSS
  • Глава 3 - Атаки без JavaScript
    • Кто сказал, что для атаки обязательно выполнять JavaScript?
    • Prototype Pollution - Эксплуатация цепочки прототипов
    • Может ли HTML влиять на JavaScript - Введение в DOM clobbering
    • Template Injection in Frontend - CSTI
    • CSS Injection - Атака с использованием только CSS (Часть 1)
    • CSS Injection - Атака с использованием только CSS (Часть 2)
    • Можно ли атаковать, используя только HTML
  • Глава 4 - Межсайтовые атаки
    • Same-origin Policy и Same-Site
    • Введение в Cross-Origin Resource Sharing (CORS)
    • Проблемы Cross-Origin безопасности
    • Cross-Site Request Forgery (CSRF)
    • Спаситель от CSRF - Same-site cookie
    • От same-site до главного site
    • Интересная и практичная Cookie Bomb
  • Глава 5 - Другие интересные темы
    • То, что вы видите, это не то, что вы получаете - Clickjacking
    • Эксплуатация MIME Sniffing
    • Атаки на цепочку поставок во фронтенде - Attacking Downstream from Upstream
    • Атаки на веб-фронтенд в Web3
    • Самая интересная атака на побочные каналы фронтенда - XSLeaks (Часть 1)
    • Самая интересная атака на побочные каналы фронтенда - XSLeaks (Часть 2)
Powered by GitBook
On this page
  • Автоматический механизм защиты: Content Security Policy
  • Правила CSP
  • Правила для script-src
  • Как определить правила CSP?
  • Как другие настраивают CSP?
  • Заключение
  1. Глава 2 - Защита и Обход для XSS

Вторая линия обороны от XSS - CSP (Content Security Policy)

Первая линия обороны от XSS - это обработка и санитизация пользовательского ввода для обеспечение безопасности содержимого. Однако это проще сказать, чем сделать, особенно для устаревших проектов с запутанным и сложным кодом. Становится сложно определить, где следует внести необходимые исправления.

Кроме того, ошибки могут произойти при написании кода, и есть три основные причины возникновения проблем безопасности:

1. Вы не знаете, что определенные действия могут привести к проблемам. 2. Вы забываете, что определенные действия могут привести к проблемам. 3. Вы знаете, что определенные действия могут привести к проблемам, но из-за сроков выполнения проекта или указаний руководства, вы предпочитаете их игнорировать.

Первая причина аналогична упомянутому ранее примеру с тегом <a href>, в котором вы, возможно, не знаете, что он может исполнять код с помощью javascript:.

Вторая причина - когда вы знаете о уязвимостях XSS и знаете, что вывод должен быть закодирован, но забываете это сделать.

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

В случае с первой причиной, когда у вас нет представления о том, где следует решить проблему, или о наличии уязвимости, как можно защититься от нее? Вот здесь нам и понадобится CSP.

Автоматический механизм защиты: Content Security Policy

CSP, сокращенно от Content Security Policy, позволяет вам устанавливать правила для вашей веб-страницы и информировать браузер, что разрешено только содержимое, которое соответствует этим правилам. Любой контент, который не соответствует, должен быть заблокирован.

Есть два способа добавления CSP на веб-страницу. Один через заголовок HTTP-ответа Content-Security-Policy, и другой через тег <meta>. Поскольку последний способ проще для демонстрации, мы сосредоточимся на нем (хотя более часто используется первый, так как некоторые правила можно установить только через него).

(Существует еще один загадочный третий метод, связанный с атрибутом csp тега <iframe>, но мы не будем здесь обсуждать это.)

Посмотрим на пример:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Security-Policy" content="script-src 'none'">
</head>
<body>
    <script>alert(1)</script>
    CSP test
</body>
</html>

На этой веб-странице CSP объявлена как script-src 'none', что означает "на этой веб-странице не разрешено никакое исполнение скрипта". Соответственно, скрипт в теле не будет выполнен. Если вы откроете инструменты разработчика, вы увидите следующее сообщение об ошибке:

> Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'none'". Either the 'unsafe-inline' keyword, a hash ('sha256-bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI='), or a nonce ('nonce-...') is required to enable inline execution.

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

Правила CSP

CSP позволяет вам определить директивы вместе с правилами. В приведенном выше примере директива script-src установлена в 'none', что в конечном итоге блокирует выполнение любого JavaScript.

Во-первых, важно отметить, что директиву script-src не следует легко интерпретировать как "атрибут src тегов script". Здесь слово "script" относится к общему понятию "скриптов", а не специально к тегам скриптов или атрибутам src.

Например, если на странице есть фрагмент HTML <a href="javascript:alert(1)">click</a>, который не имеет тега скрипта или атрибута src, он все равно будет заблокирован CSP, и будет отображено сообщение об ошибке. Это происходит потому, что script-src 'none' означает "block the execution of any JavaScript", будь это через теги скриптов, обработчики событий или псевдо-протокол javascript:.

Итак, какие доступны директивы?

Другие часто используемые директивы включают: 1. script-src: Управляет JavaScript 2. style-src: Управляет CSS 3. font-src: Управляет шрифтами 4. img-src: Управляет изображениями 5. connect-src: Управляет подключениями (fetch, XMLHttpRequest, WebSocket и т.д.) 6. media-src: Управляет видео и аудио 7. frame-src: Управляет фреймами и iframes 8. base-uri: Управляет использованием `<base>` 9. form-action: Управляет действиями форм 10. frame-ancestors: Управляет тем, какие страницы могут внедрять текущую страницу 11. report-uri: Будет обсуждено позже 12. navigate-to: Управляет тем, куда может направляться страница

Существует много видов, верно? И этот список может изменяться. Например, navigate-to в конце - это более новая функция, которая еще не поддерживается современными браузерами.

Так что какие возможны правила для каждого из них? В зависимости от директив могут использоваться разные правила.

Наиболее часто используемые правила таковы:

Например, script-src * по сути эквивалентно отсутствию какого-либо правила (разрешает все URL, но стоит отметить, что встроенный скрипт все равно блокируется), в то время как script-src 'none' полностью блокирует выполнение любого JavaScript.

Более того, некоторые правила можно объединять. На практике вы часто видите правила вроде этого:

script-src 'self' cdn.example.com www.google-analytics.com *.facebook.net

Полная CSP - это комбинация этих правил, с директивами, разделенными ;, вот так:

default-src 'none'; script-src 'self' cdn.example.com www.google-analytics.com *.facebook.net; img-src *;

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

Правила для script-src

Помимо указания URL ресурсов для загрузки, существуют другие правила, которые можно использовать.

Например, после установки CSP, встроенные скрипты и eval блокируются по умолчанию. Заблокированы следующие встроенные скрипты:

1. Код, прямо размещенный внутри тегов <script> (должен быть загружен из внешнего источника с использованием <script src>) 2. Обработчики событий, написанные в HTML, такие как onclick 3. Псевдопротокол javascript:

Чтобы разрешить использование встроенных скриптов, необходимо добавить правило 'unsafe-inline'.

И если вы хотите выполнить код, как если бы он был eval, вам нужно добавить правило 'unsafe-eval'. Некоторые люди могут знать, что setTimeout также может выполнять код в виде строки, вот так:

setTimeout('alert(1)')

Аналогично, setInterval, Function и другие могут добиться того же, но все они требуют использования правила 'unsafe-eval'.

В дополнение к этому, есть еще 'nonce-xxx', что означает генерацию случайной строки на бэкенде, например, a2b5zsa19c. Тогда можно загрузить тег скрипта с nonce=a2b5zsa19c:

<!-- Allowed -->
<script nonce="a2b5zsa19c">
    alert(1);
</script>

<!-- Not allowed -->
<script>
    alert(1);
</script>

Есть также похожее правило 'sha256-abc...', которое разрешает определенные встроенные скрипты на основе их хэша. Например, если мы возьмем alert(1) и рассчитаем его sha256-хэш, мы получим двоичное значение, которое при кодировании base64 становится bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI=. Поэтому в приведенном ниже примере будет загружен только скрипт с точным содержимым alert(1), в то время как другие не будут загружены:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Security-Policy" content="script-src 'sha256-bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI='">
</head>
<body>
    <!-- Allowed -->
    <script>alert(1)</script>
    
    <!-- Not Allowed -->
    <script>alert(2)</script>
    
    <!-- An extra space is also not allowed because it results in a different hash value -->
    <script>alert(1) </script>
</body>
</html>

Наконец, существует еще одно, которое может быть использовано, 'strict-dynamic', что означает: "Scripts that comply with the rules can load other scripts without being restricted by CSP." Вот так:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Security-Policy" content="script-src 'nonce-rjg103rj1298e' 'strict-dynamic'">
</head>
<body>
    <script nonce="rjg103rj1298e">
        const element = document.createElement('script');
        element.src = 'https://example.com';
        document.body.appendChild(element);
    </script>
</body>
</html>

В установленном нами CSP для скриптов разрешается только nonce-rjg103rj1298e, и не разрешаются никакие другие источники. Однако скрипты, добавленные из <script nonce=rjg103rj1298e>, не ограничены и могут динамически добавлять скрипты из других источников. Это функция 'strict-dynamic'.

Как определить правила CSP?

При настройке CSP обычно начинают с default-src 'self', что позволяет по умолчанию использовать ресурсы с того же источника.

Теперь давайте обработаем наиболее важные скрипты. Обычно наиболее приоритетной задачей является избегание использования 'unsafe-inline' и 'unsafe-eval', так как наличие этих двух параметров не имеет большого значения по сравнению с отсутствием CSP вообще.

В чем состоит цель добавления CSP? Его цель - служить второй линией обороны против XSS-атак. Однако, если добавить 'unsafe-inline', это подрывает всю защиту, так как простая вставка <svg onload=alert(1)> может выполнить код.

Но в реальности обычно есть некоторые существующие встроенные скрипты, которые заставляют нас добавить 'unsafe-inline'. Здесь я представлю общий подход. Например, с Google Analytics они попросят вас добавить следующий код на вашу веб-страницу:

<script async src="https://www.googletagmanager.com/gtag/js?id=UA-XXXXXXXX-X"></script>
<script>
    window.dataLayer = window.dataLayer || [];
    function gtag() {
        dataLayer.push(arguments);
    }
    gtag('js', new Date());
    gtag('config', 'UA-XXXXXXXX-X');
</script>

1. Добавить nonce в этот конкретный скрипт. 2. Вычислить хэш этого скрипта и добавить такое правило как sha256-xxx.

Оба этих решения позволяют выполнить определенные встроенные скрипты без использования разрешения 'unsafe-inline'. Кроме того, в официальной документации также напоминается, что если мы хотим использовать функцию "Custom JavaScript Variable", мы должны включить 'unsafe-eval', чтобы она работала.

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

В первой половине статьи упоминался директивный вызов "report-uri", который является очень грамотной функцией. Если CSP неправильно настроен, он может блокировать обычные ресурсы, вызывая сбои в работе веб-сайта или неработоспособность определенных функций. Это будет потерей.

Поэтому есть еще один заголовок под названием Content-Security-Policy-Report-Only, что означает, что вы можете установить CSP, но он не будет блокировать что-либо. Вместо этого он будет отправлять отчет на указанный URL, когда загружается ресурс, нарушающий правила.

С помощью этой функции мы можем наблюдать любые нарушения CSP и проверять наличие неправильных настроек, изучая логи. Как только все подтверждено как должное, мы можем переключиться на использование Content-Security-Policy.

Как другие настраивают CSP?

Видели ли вы когда-нибудь длинную строку CSP?

Давайте посмотрим на CSP на главной странице GitHub, чтобы получить представление о том, как выглядит длинная строка:

default-src  'none';base-uri  'self'; child-src  github.com/assets-cdn/worker/  gist.github.com/assets-cdn/worker/;connect-src  'self'  uploads.github.com  objects-origin.githubusercontent.com  www.githubstatus.com  collector.github.com  raw.githubusercontent.com  api.github.com  github-cloud.s3.amazonaws.com  github-production-repository-file-5c1aeb.s3.amazonaws.com  github-production-upload-manifest-file-7fdce7.s3.amazonaws.com  github-production-user-asset-6210df.s3.amazonaws.com  cdn.optimizely.com  logx.optimizely.com/v1/events  *.actions.githubusercontent.com  productionresultssa0.blob.core.windows.net/  productionresultssa1.blob.core.windows.net/  productionresultssa2.blob.core.windows.net/  productionresultssa3.blob.core.windows.net/  productionresultssa4.blob.core.windows.net/  wss://*.actions.githubusercontent.com  github-production-repository-image-32fea6.s3.amazonaws.com  github-production-release-asset-2e65be.s3.amazonaws.com  insights.github.com  wss://alive.github.com github.githubassets.com; font-src  github.githubassets.com;form-action  'self'  github.com  gist.github.com  objects-origin.githubusercontent.com;frame-ancestors  'none';frame-src  viewscreen.githubusercontent.com  notebooks.githubusercontent.com;img-src  'self'  data:  github.githubassets.com  media.githubusercontent.com  camo.githubusercontent.com  identicons.github.com  avatars.githubusercontent.com  github-cloud.s3.amazonaws.com  objects.githubusercontent.com  objects-origin.githubusercontent.com  secured-user-images.githubusercontent.com/  user-images.githubusercontent.com/  private-user-images.githubusercontent.com  opengraph.githubassets.com  github-production-user-asset-6210df.s3.amazonaws.com  customer-stories-feed.github.com  spotlights-feed.github.com  *.githubusercontent.com;manifest-src  'self';media-src  github.com  user-images.githubusercontent.com/  secured-user-images.githubusercontent.com/  private-user-images.githubusercontent.com  github.githubassets.com;script-src  github.githubassets.com;style-src  'unsafe-inline'  github.githubassets.com;upgrade-insecure-requests;worker-src  github.com/assets-cdn/worker/  gist.github.com/assets-cdn/worker/

Теперь давайте посмотрим на Facebook:

default-src  *  data:  blob:  'self'  'wasm-unsafe-eval'script-src  *.facebook.com  *.fbcdn.net  *.facebook.net  *.google-analytics.com  *.google.com  127.0.0.1:*  'unsafe-inline'  blob:  data:  'self'  'wasm-unsafe-eval'style-src  data:  blob:  'unsafe-inline'  *connect-src  secure.facebook.com  dashi.facebook.com  dashi-pc.facebook.com  graph-video.facebook.com  streaming-graph.facebook.com  z-m-graph.facebook.com  z-p3-graph.facebook.com  z-p4-graph.facebook.com  rupload.facebook.com  upload.facebook.com  vupload-edge.facebook.com  vupload2.facebook.com  z-p3-upload.facebook.com  z-upload.facebook.com  graph.facebook.com  'self'  *.fbcdn.net  wss://*.fbcdn.net  attachment.fbsbx.com  blob:  data:  *.cdninstagram.com  *.up.facebook.com  wss://edge-chat-latest.facebook.com  wss://edge-chat.facebook.com  edge-chat.facebook.com  edge-chat-latest.facebook.com  wss://gateway.facebook.com  *.facebook.com/rsrc.php/  https://api.mapbox.com  https://*.tiles.mapbox.comblock-all-mixed-contentupgrade-insecure-requests;

Хотя это тоже длинная строка, вы можете заметить, что для скриптов разрешено использование 'unsafe-inline', что является менее безопасным подходом. Если вы вставите эту CSP в CSP Evaluator, упомянутую выше, он покажет много красных флагов:

Заключение

Я лично рекомендую использование CSP. Как только она будет настроена, это добавляет дополнительный уровень защиты, давая нам возможность уменьшить любые проблемы. Блокируя XSS через CSP, мы можем минимизировать ущерб.

Кроме того, порог вступления в настройку CSP не высок. Вы можете начать с режима "report only", наблюдать и корректировать правила CSP для вашего сайта, и убедиться, что это не влияет на обычных пользователей, прежде чем запустить его в живую.

Наконец, давайте проведем небольшой викторину, на которую будет дан ответ в будущих разделах.

PreviousПервая линия обороны от XSS - SanitizationNextТретья линия обороны против XSS - сокращение области воздействия

Last updated 8 months ago

Самая важная - это default-src, которая представляет собой правила по умолчанию. Например, если script-src не установлен, будут использоваться правила из default-src. Однако важно заметить, что некоторые директивы не откатываются к default-src, такие как base-uri или form-action. Полный список можно найти здесь:

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

1. * - Разрешает все URL, кроме data:, blob: и filesystem:. 2. 'none' - Ничего не разрешает. 3. 'self' - Разрешает только ресурсы того же происхождения. 4. https: - Разрешает все ресурсы HTTPS. 5. - Разрешает определенные домены (и HTTP, и HTTPS). 6. - Разрешает определенные происхождения (только HTTPS).

Иногда скрипты размещаются на том же origin, поэтому необходимо self. Некоторые скрипты размещаются на CDN, поэтому требуется . И если используются Google Analytics и Facebook SDK, требуется *. для загрузки их JavaScript.

Это встроенный скрипт, которого мы хотим избежать. Итак, что мы можем сделать? Официальная документация, предоставляемая Google, , упоминает два решения:

Если вы не уверены в безопасности ваших правил CSP, вы можете воспользоваться сайтом, предоставленным компанией Google под названием . Он обнаружит любые ошибки в вашей настройке CSP и оценит безопасность, как показано здесь:

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

Программа Bug Bounty GitHub также имеет специальную категорию, называемую . Если вы сможете обойти CSP и выполнить код, даже если вы не нашли место для внедрения HTML, это все равно будет учтено.

После прочтения этой статьи, Боб взглянул на свой проект и обнаружил, что все файлы JavaScript появляются из пакетов . Поэтому он добавил следующую CSP. В чем проблема с частью script-src?

Content-Security-Policy: script-src ;

Директива default-src
MDN: Content-Security-Policy
Content Security Policy Reference
example.com
https://example.com
cdn.example.com
www.google-analytics.com
facebook.net
Использование инструмента управления кодом с политикой безопасности контента
CSP Evaluator
github.githubassets.com
GitHub CSP
https://unpkg.com
https://unpkg.com