Content Security Policy (CSP, политика защиты контента) — это механизм обеспечения безопасности, с помощью которого можно защищаться от атак с внедрением контента, например, межсайтового скриптинга (CSS, cross site scripting). CSP описывает безопасные источники загрузки ресурсов, устанавливает правила использования встроенных стилей, скриптов, а также динамической оценки JavaScript — например, с помощью eval. Загрузка с ресурсов, не входящих в «белый список», блокируется.
Принцип действия
CSP сейчас является кандидатом в рекомендации консорциума W3C. Для использования политики страница должна содержать HTTP-заголовок Content-Security-Policy
с одной и более директивами, которые представляют собой «белые списки». В версии 1.0 поддерживаются следующие директивы:
default-src
script-src
object-src
style-src
img-src
media-src
frame-src
font-src
connect-src
В default-src
перечисляются разрешённые источники по умолчанию для остальных директив. Если какая-то директива не указана в заголовке, то политика применяется согласно списку default-src
.
Для всех директив действуют следующие правила:
- Для ссылки на текущий домен используется
self
. - В перечне URL адреса разделяются пробелами.
- Если в рамках данной директивы ничего не должно загружаться, то применяется
none
. Например,object-src 'none'
запрещает загрузку любых плагинов, включая Java и Flash.
Простейший пример политики, разрешающей загрузку ресурсов только указанного домена:
Content-Security-Policy: default-src 'self';
Попытка загрузки ресурсов из иных доменов будут пресекаться браузером с выдачей уведомления в консоли:
По умолчанию CSP ограничивает исполнение JavaScript путём запрета встроенных скриптов и динамической оценки кода. В комбинации с «белыми списками» это позволяет предотвращать атаки с внедрением контента. Например, XSS-атаку с внедрением тэга инлайн-скрипта:
Загрузка внешних скриптов, которые не включены в CSP, также будет пресечена:
На данный момент в перечне URL нельзя прописывать пути (http://cdn.example.com/path
), можно лишь перечислять сами домены (http://cdn.example.com
). Зато поддерживаются символы подстановки, так что вы можете описать сразу все поддомены (http://*.example.com
).
Директивы не наследуют права от предыдущих директив. Каждая директива, включённая в заголовок CSP, должна содержать перечень разрешённых доменов/поддоменов. В следующем примере default-src
и style-src
содержат ключевое слово self
, а script-src
и style-src
содержат домен http://cdn.example.com
:
Content-Security-Policy: default-src 'self'; style-src 'self' http://cdn.example.com; script-src http://cdn.example.com;
Если вам нужно указать хосты для загрузки данных, то нужно указать ключевое слово data: img-src 'data:';
.
Помимо списков доменов, директивы script-src
и style-src
поддерживают ключевые слова unsafe-inline
и unsafe-eval
.
unsafe-inline
используется для разрешения инлайн-стилей и скриптов<style>
и<script>
. Также это ключевое слово разрешает инлайн-атрибуты CSSstyle
, инлайн-обработчики событий (onclick, onmouseover и т.д.) и javascript-ссылки наподобие<a href="javascript:foobar()">
. CSP работает по принципу «если что-то не упомянуто, значит запрещено». То есть при отсутствии ключевого словаunsafe-inline
все инлайн-тэги<style>
и<script>
будут блокироваться.unsafe-eval
используется только в директивеscript-src
. Если это ключевое слово не указано, то блокируется любая динамическая оценка кода, включая использованиеeval
, конструктор функций и передачу строковых вsetTimeout
иsetInterval
.
Поддержка браузерами
На текущий момент большинство браузеров и их версий уже поддерживают CSP 1.0. IE снова отличился: в 10 и 11 версиях обеспечена лишь частичная поддержка с помощью заголовка X-Content-Security-Policy
. Судя по всему, поддерживается только опциональная директива sandbox.
Получение отчётов о нарушениях CSP
Как уже упоминалось, сообщения обо всех нарушениях политики безопасности логгируются в консоли браузера. Это удобно, пока вы только разрабатываете сайт, но после развёртывания нужен более практичный способ получения отчётов о нарушениях. Для этого можно использовать директиву report-uri
. Каждый раз, когда регистрируется нарушение CSP, директива отправляет на указанный адрес запрос HTTP POST. В теле запроса содержится JSON-объект, в котором указаны все необходимые подробности.
Допустим, у нас есть такая CSP:
Content-Security-Policy: default-src 'self'; report-uri: https://example.com/csp/report;
Это означает, что браузер может загружать ресурсы только с нашего собственного домена. Но нам нужно использовать сервис Google Analytics, который будет пытаться скачивать JavaScript с www.google-analytics.com. А это уже нарушение нашей CSP. В этом случае report-uri
отправит запрос со следующим JSON:
{ "csp-report": { "blocked-uri:" "http://ajax.googleapis.com" "document-uri:" "http://example.com/index.html" "original-policy": "default-src 'self'; report-uri http://example.com/csp/report" "referrer:" "" "violated-directive": "default-src 'self'" } }
Content-Security-Policy-Report-Only
Если вы пока не уверены, стоит ли внедрять у себя CSP, то можно попробовать вместо заголовка Content-Security-Policy
использовать Content-Security-Policy-Report-Only
. В этом случае CSP будет регистрировать нарушения без какого-либо блокирования ресурсов. Можно даже использовать одновременно Content-Security-Policy
и Content-Security-Policy-Report-Only
, обкатывая на второй те или иные конфигурации.
Прописывание заголовка
HTTP-заголовок можно прописать прямо в конфигурационных файлах на сервере:
# Apache config Header set Content-Security-Policy "default-src 'self';" # IIS Web.config <system.webServer> <httpProtocol> <customHeaders> <add name="Content-Security-Policy" value="default-src 'self';" /> </customHeaders> </httpProtocol> </system.webServer> # nginx conf file add_header Content-Security-Policy "default-src 'self';";
Также многие языки программирования и фреймворки позволяют добавлять заголовки программно (например, PHP, Node.js):
# PHP example header("Content-Security-Policy: default-src 'self'");
# Node.js example request.setHeader("Content-Security-Policy", "default-src 'self'");
CSP в дикой природе
Давайте посмотрим, как CSP внедрён в Facebook:
default-src *; script-src https://*.facebook.com http://*.facebook.com https://*.fbcdn.net http://*.fbcdn.net *.facebook.net *.google-analytics.com *.virtualearth.net *.google.com 127.0.0.1:* *.spotilocal.com:* 'unsafe-inline' 'unsafe-eval' https://*.akamaihd.net http://*.akamaihd.net *.atlassolutions.com; style-src * 'unsafe-inline'; connect-src https://*.facebook.com http://*.facebook.com https://*.fbcdn.net http://*.fbcdn.net *.facebook.net *.spotilocal.com:* https://*.akamaihd.net wss://*.facebook.com:* ws://*.facebook.com:* http://*.akamaihd.net https://fb.scanandcleanlocal.com:* *.atlassolutions.com http://attachment.fbsbx.com https://attachment.fbsbx.com;
Обратите внимание на использование символов подстановки для описания поддоменов, а также номеров портов в connect-src
.
А теперь вариант Twitter:
default-src https:; connect-src https:; font-src https: data:; frame-src https: twitter:; frame-ancestors https:; img-src https: data:; media-src https:; object-src https:; script-src 'unsafe-inline' 'unsafe-eval' https:; style-src 'unsafe-inline' https:; report-uri https://twitter.com/i/csp_report?a=NVQWGYLXFVZXO2LGOQ%3D%3D%3D%3D%3D%3D&ro=false;
Здесь везде прописаны https:
, то есть принудительно используется SSL.
CSP Level 2
Также является кандидатом в рекомендации. В CSP Level 2 сделаны следующий нововведения:
base-uri
: позволяет документу манипулировать базовым URI страницы.- Вместо
frame-src
теперь применяетсяchild-src
. form-action
: позволяет документу размещать HTML-формы.frame-ancestors
: регламентирует способ встраивания данного документа в другие документы. Работает как заголовок X-Frame-Options, для замены которого, вероятно, и предназначен.plugin-types
: разрешает загрузку конкретных плагинов — Flash, Java, Silverlight и т.д.
В JSON, содержащихся в отчётах о нарушениях, появились два новых поля:
effective-directive
: здесь указано название директивы, которая была нарушена.status-code
: HTTP-код состояния запрошенного ресурса. Если нарушающий запрос делался не через HTTP, то ставится 0.
Также в CSP Level 2 появилась возможность разрешать инлайн-скрипты и стили с помощью nonce-значений и хэшей.
Nonce — это генерируемая случайным образом на сервере строковая переменная. Она добавляется в CSP-заголовок:
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-Xiojd98a8jd3s9kFiDi29Uijwdu';
и в тэг инлайн-скрипта:
<script> console.log("Script won't run as it doesn't contain a nonce attribute"); </script> <script nonce="Eskdikejidojdk978Ad8jf"> console.log("Script won't run as it has an invalid nonce"); </script> <script nonce="Xiojd98a8jd3s9kFiDi29Uijwdu"> console.log('Script runs as the nonce matches the nonce in the HTTP header'); </script>
Если вы захотите использовать хэши, то сначала их нужно сгенерировать на сервере и включить в заголовок CSP, соответственно в директивы style-src
или script-src
. Перед рендерингом страницы браузер вычисляет хэш скрипта/стиля, и если он совпадает с указанным, то выполнение разрешается.
Пример хэша, сгенерированного из строковой console.log('Hello, SitePoint');
с помощью алгоритма Sha256 (base64).
Content-Security-Policy: default-src 'self'; script-src 'self' 'sha256-V8ghUBat8RY1nqMBeNQlXGceJ4GMuwYA55n3cYBxxvs=';
Во время рендеринга страницы браузер для каждого инлайн-скрипта вычисляет хэши и сравнивает с перечисленными в CSP. Приведённый выше хэш позволяет выполнить скрипт:
<script>console.log('Hello, SitePoint');</script>
Обратите внимание, что пробелы имеют значение, так что эти скрипты выполнены не будут:
<script> console.log('Hello, SitePoint');</script>
<script>console.log('Hello, SitePoint'); </script>
В заключение
Если у вас есть опыт использования CSP, — положительный или отрицательный, — поделитесь, пожалуйста, в комментариях. Также было бы интересно узнать о нестандартных задачах, которые вы решали с помощью CSP.
ссылка на оригинал статьи http://habrahabr.ru/post/271575/
Добавить комментарий