Политика безопасности контента может значительно снизить риск и влияние атак с использованием межсайтовых сценариев в современных браузерах.
Модель безопасности Интернета основана на политике единого происхождения . Например, код https://mybank.com
должен иметь доступ только к данным https://mybank.com
, а https://evil.example.com
никогда не должен иметь доступ. Теоретически каждый источник изолирован от остальной сети, что дает разработчикам безопасную «песочницу» для встраивания. Однако на практике злоумышленники нашли несколько способов подорвать систему.
Например, атаки с использованием межсайтовых сценариев (XSS) обходят политику одного и того же источника, заставляя сайт доставлять вредоносный код вместе с предполагаемым контентом. Это огромная проблема, поскольку браузеры доверяют всему коду, который отображается на странице, как законной части источника безопасности этой страницы. Шпаргалка XSS — это старый, но репрезентативный набор методов, которые злоумышленник может использовать для нарушения этого доверия путем внедрения вредоносного кода. Если злоумышленник вообще успешно внедрит какой-либо код, он скомпрометирует пользовательский сеанс и получит доступ к личной информации.
На этой странице описана Политика безопасности контента (CSP) как стратегия снижения риска и воздействия XSS-атак в современных браузерах.
Компоненты CSP
Чтобы внедрить эффективную CSP, выполните следующие шаги:
- Используйте списки разрешенных, чтобы сообщить клиенту, что разрешено, а что нет.
- Узнайте, какие директивы доступны.
- Изучите ключевые слова, которые они берут.
- Ограничьте использование встроенного кода и
eval()
. - Сообщайте о нарушениях политики на свой сервер, прежде чем применять их.
Белые списки источников
XSS-атаки используют неспособность браузера отличить скрипт, являющийся частью вашего приложения, от скрипта, злонамеренно внедренного третьей стороной. Например, кнопка Google +1 внизу этой страницы загружает и выполняет код из https://apis.google.com/js/plusone.js
в контексте источника этой страницы. Мы доверяем этому коду, но не можем ожидать, что браузер сам определит, что код с apis.google.com
безопасен для запуска, а код с apis.evil.example.com
, вероятно, нет. Браузер с радостью загружает и выполняет любой код, запрашиваемый страницей, независимо от источника.
HTTP-заголовок Content-Security-Policy
CSP позволяет создать список разрешенных источников доверенного контента и сообщает браузеру выполнять или отображать только ресурсы из этих источников. Даже если злоумышленник сможет найти дыру для внедрения сценария, сценарий не будет соответствовать списку разрешений и, следовательно, не будет выполнен.
Мы доверяем apis.google.com
предоставлять действительный код и уверены, что сделаем то же самое. Вот пример политики, которая позволяет сценариям выполняться только в том случае, если они получены из одного из этих двух источников:
Content-Security-Policy: script-src 'self' https://apis.google.com
script-src
— это директива, которая управляет набором привилегий, связанных со сценарием, для страницы. Этот заголовок 'self'
является одним допустимым источником скрипта, а https://apis.google.com
— другим. Теперь браузер может загружать и выполнять JavaScript с apis.google.com
через HTTPS, а также из источника текущей страницы, но не из какого-либо другого источника. Если злоумышленник внедряет код на ваш сайт, браузер выдает ошибку и не запускает внедренный скрипт.
Политика применяется к широкому спектру ресурсов
CSP предоставляет набор директив политики, которые обеспечивают детальный контроль над ресурсами, которые разрешено загружать странице, включая script-src
из предыдущего примера.
В следующем списке представлены остальные директивы ресурсов уровня 2. Спецификация уровня 3 уже разработана, но она практически не реализована в основных браузерах.
-
base-uri
- Ограничивает URL-адреса, которые могут отображаться в элементе
<base>
страницы. -
child-src
- Перечисляет URL-адреса рабочих процессов и встроенного содержимого фрейма. Например,
child-src https://youtube.com
позволяет встраивать видео с YouTube, но не из других источников. -
connect-src
- Ограничивает источники, к которым вы можете подключиться с помощью XHR, WebSockets и EventSource.
-
font-src
- Указывает источники, которые могут обслуживать веб-шрифты. Например, вы можете разрешить использование веб-шрифтов Google, используя
font-src https://themes.googleusercontent.com
. -
form-action
- Перечисляет допустимые конечные точки для отправки из тегов
<form>
. -
frame-ancestors
- Указывает источники, в которые можно встроить текущую страницу. Эта директива применяется к тегам
<frame>
,<iframe>
,<embed>
и<applet>
. Его нельзя использовать в тегах<meta>
или для ресурсов HTML. -
frame-src
- Эта директива устарела на уровне 2, но восстановлена на уровне 3. Если она отсутствует, браузер возвращается к
child-src
. -
img-src
- Определяет исходные изображения, из которых можно загружать.
-
media-src
- Ограничивает источники, которым разрешено доставлять видео и аудио.
-
object-src
- Позволяет контролировать Flash и другие плагины.
-
plugin-types
- Ограничивает типы плагинов, которые может вызывать страница.
-
report-uri
- Указывает URL-адрес, на который браузер отправляет отчеты при нарушении политики безопасности контента. Эту директиву нельзя использовать в тегах
<meta>
. -
style-src
- Ограничивает источники, из которых страница может использовать таблицы стилей.
-
upgrade-insecure-requests
- Поручает агентам пользователя перезаписать схемы URL-адресов, заменив HTTP на HTTPS. Эта директива предназначена для веб-сайтов с большим количеством старых URL-адресов, которые необходимо переписать.
-
worker-src
- Директива CSP уровня 3, которая ограничивает URL-адреса, которые могут быть загружены в качестве рабочего, общего или сервисного работника. По состоянию на июль 2017 года эта директива имеет ограниченное применение .
По умолчанию браузер загружает связанный ресурс из любого источника без ограничений, если только вы не установили политику с определенной директивой. Чтобы переопределить значение по умолчанию, укажите директиву default-src
. Эта директива определяет значения по умолчанию для любой неуказанной директивы, оканчивающейся на -src
. Например, если вы установите для default-src
значение https://example.com
и не укажете директиву font-src
, вы сможете загружать шрифты только с https://example.com
.
Следующие директивы не используют default-src
в качестве запасного варианта. Помните, что не установить их — это то же самое, что разрешить что-либо:
-
base-uri
-
form-action
-
frame-ancestors
-
plugin-types
-
report-uri
-
sandbox
Базовый синтаксис CSP
Чтобы использовать директивы CSP, перечислите их в заголовке HTTP, разделив их двоеточиями. Обязательно перечислите все необходимые ресурсы определенного типа в одной директиве следующим образом:
script-src https://host1.com https://host2.com
Ниже приведен пример нескольких директив, в данном случае для веб-приложения, которое загружает все свои ресурсы из сети доставки контента по адресу https://cdn.example.net
и не использует фреймовый контент или плагины:
Content-Security-Policy: default-src https://cdn.example.net; child-src 'none'; object-src 'none'
Детали реализации
Современные браузеры поддерживают заголовок Content-Security-Policy
без префикса. Это рекомендуемый заголовок. Заголовки X-WebKit-CSP
и X-Content-Security-Policy
которые вы можете увидеть в онлайн-руководствах, устарели.
CSP определяется постранично. Вам нужно будет отправлять HTTP-заголовок с каждым ответом, который вы хотите защитить. Это позволяет вам точно настроить политику для конкретных страниц в соответствии с их конкретными потребностями. Например, если на одном наборе страниц вашего сайта есть кнопка +1, а на других нет, вы можете разрешить загрузку кода кнопки только при необходимости.
Список источников для каждой директивы является гибким. Вы можете указать источники по схеме ( data:
, https:
) или по специфичности от имени хоста ( example.com
, что соответствует любому источнику на этом хосте: любая схема, любой порт) до полного URI ( https://example.com:443
, что соответствует только HTTPS, только example.com
и только порту 443). Подстановочные знаки принимаются, но только в виде схемы, порта или в крайнем левом положении имени хоста: *://*.example.com:*
будет соответствовать всем поддоменам example.com
(но не самому example.com
), по любой схеме, на любом порту.
Список источников также принимает четыре ключевых слова:
-
'none'
не соответствует ничему. -
'self'
соответствует текущему источнику, но не его поддоменам. -
'unsafe-inline'
позволяет использовать встроенный JavaScript и CSS. Дополнительные сведения см. в разделе Избегайте встроенного кода . -
'unsafe-eval'
поддерживает механизмы преобразования текста в JavaScript, такие какeval
. Дополнительную информацию см. в разделе Избегайтеeval()
.
Эти ключевые слова требуют одинарных кавычек. Например, script-src 'self'
(с кавычками) разрешает выполнение JavaScript с текущего хоста; script-src self
(без кавычек) разрешает JavaScript с сервера с именем « self
» (а не с текущего хоста), что, вероятно, не то, что вы имели в виду.
Песочница для ваших страниц
Есть еще одна директива, о которой стоит поговорить: sandbox
. Он немного отличается от других, которые мы рассматривали, поскольку накладывает ограничения на действия, которые может выполнять страница, а не на ресурсы, которые страница может загружать. Если директива sandbox
присутствует, страница обрабатывается так, как если бы она была загружена внутри <iframe>
с атрибутом sandbox
. Это может иметь широкий спектр последствий для страницы: принудительное присвоение странице уникального источника и предотвращение отправки формы, среди прочего. Это немного выходит за рамки этой страницы, но вы можете найти полную информацию о допустимых атрибутах песочницы в разделе «Песочница» спецификации HTML5 .
Мета-тег
Предпочтительным механизмом доставки CSP является заголовок HTTP. Однако может быть полезно установить политику на странице непосредственно в разметке. Сделайте это, используя тег <meta>
с атрибутом http-equiv
:
<meta http-equiv="Content-Security-Policy" content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'">
Это нельзя использовать для frame-ancestors
, report-uri
или sandbox
.
Избегайте встроенного кода
Какими бы мощными ни были белые списки на основе источника, используемые в директивах CSP, они не могут решить самую большую угрозу, создаваемую XSS-атаками: внедрение встроенных скриптов. Если злоумышленник может внедрить тег сценария, который непосредственно содержит вредоносную полезную нагрузку (например <script>sendMyDataToEvilDotCom()</script>
), браузер не сможет отличить его от законного встроенного тега сценария. CSP решает эту проблему, полностью запрещая встроенные сценарии.
Этот запрет распространяется не только на скрипты, встроенные непосредственно в теги script
, но также на встроенные обработчики событий и URL-адреса javascript:
:. Вам потребуется переместить содержимое тегов script
во внешний файл и заменить URL-адреса javascript:
и <a ... onclick="[JAVASCRIPT]">
соответствующими вызовами addEventListener()
. Например, вы можете переписать следующее:
<script>
function doAmazingThings() {
alert('YOU ARE AMAZING!');
}
</script>
<button onclick='doAmazingThings();'>Am I amazing?</button>
что-то вроде:
<!-- amazing.html -->
<script src='amazing.js'></script>
<button id='amazing'>Am I amazing?</button>
// amazing.js
function doAmazingThings() {
alert('YOU ARE AMAZING!');
}
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('amazing')
.addEventListener('click', doAmazingThings);
});
Переписанный код не только совместим с CSP, но и соответствует лучшим практикам веб-дизайна. Встроенный JavaScript смешивает структуру и поведение таким образом, что код становится запутанным. Также сложнее кэшировать и компилировать. Перемещение вашего кода на внешние ресурсы повышает производительность ваших страниц.
Перемещение встроенных тегов и атрибутов style
во внешние таблицы стилей также настоятельно рекомендуется для защиты вашего сайта от атак с целью кражи данных с помощью CSS.
Как временно разрешить встроенные скрипты и стили
Вы можете включить встроенные скрипты и стили, добавив 'unsafe-inline'
в качестве разрешенного источника в директиве script-src
или style-src
. CSP уровня 2 также позволяет добавлять определенные встроенные сценарии в список разрешенных, используя либо криптографический одноразовый номер (число, используемое один раз), либо хэш, как показано ниже.
Чтобы использовать nonce, присвойте тегу скрипта атрибут nonce. Его значение должно совпадать с одним в списке доверенных источников. Например:
<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
// Some inline code I can't remove yet, but need to as soon as possible.
</script>
Добавьте nonce в директиву script-src
после ключевого слова nonce-
:
Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
Одноразовые номера должны генерироваться заново для каждого запроса страницы, и они не должны быть угадываемыми.
Хэши работают аналогичным образом. Вместо добавления кода в тег скрипта создайте SHA-хеш самого скрипта и добавьте его в директиву script-src
. Например, если на вашей странице содержится следующее:
<script>alert('Hello, world.');</script>
Ваша политика должна содержать следующее:
Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='
Префикс sha*-
указывает алгоритм, генерирующий хэш. В предыдущем примере используется sha256-
, но CSP также поддерживает sha384-
и sha512-
. При создании хеша опустите теги <script>
. Заглавные буквы и пробелы имеют значение, включая ведущие и конечные пробелы.
Решения для генерации хэшей SHA доступны на любом количестве языков. Используя Chrome 40 или более позднюю версию, вы можете открыть DevTools, а затем перезагрузить страницу. На вкладке «Консоль» отображаются сообщения об ошибках с правильным хешем SHA-256 для каждого встроенного скрипта.
Избегайте eval()
Даже если злоумышленник не может внедрить скрипт напрямую, он может обманом заставить ваше приложение преобразовать входной текст в исполняемый код JavaScript и выполнить его от его имени. eval()
, new Function()
, setTimeout([string], …)
и setInterval([string], ...)
— все это векторы, которые злоумышленники могут использовать для выполнения вредоносного кода через внедренный текст. Реакция CSP по умолчанию на этот риск — полная блокировка всех этих векторов.
Это оказывает следующее влияние на способ создания приложений:
- Вы должны анализировать JSON, используя встроенный
JSON.parse
, а не полагаться наeval
. Безопасные операции JSON доступны в каждом браузере, начиная с IE8 . Вы должны переписать все вызовы
setTimeout
илиsetInterval
, используя встроенные функции вместо строк. Например, если ваша страница содержит следующее:setTimeout("document.querySelector('a').style.display = 'none';", 10);
Перепишите его как:
setTimeout(function () { document.querySelector('a').style.display = 'none'; }, 10); ```
Избегайте встроенных шаблонов во время выполнения. Многие библиотеки шаблонов часто используют
new Function()
для ускорения создания шаблонов во время выполнения, что позволяет оценивать вредоносный текст. Некоторые платформы поддерживают CSP «из коробки», возвращаясь к надежному синтаксическому анализатору при отсутствииeval
. Директива ng-csp AngularJS является хорошим примером этого. Однако вместо этого мы рекомендуем использовать язык шаблонов, который предлагает предварительную компиляцию, например Handlebars . Предварительная компиляция шаблонов может сделать работу пользователя даже быстрее, чем самая быстрая реализация во время выполнения, а также сделать ваш сайт более безопасным.
Если eval()
или другие функции преобразования текста в JavaScript необходимы для вашего приложения, вы можете включить их, добавив 'unsafe-eval'
в качестве разрешенного источника в директиве script-src
. Мы настоятельно не рекомендуем это делать из-за риска внедрения кода.
Сообщить о нарушениях правил
Чтобы уведомить сервер об ошибках, которые могут привести к вредоносному внедрению, вы можете указать браузеру отправлять отчеты о нарушениях в формате JSON POST
в место, указанное в директиве report-uri
:
Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;
Эти отчеты выглядят следующим образом:
{
"csp-report": {
"document-uri": "http://example.org/page.html",
"referrer": "http://evil.example.com/",
"blocked-uri": "http://evil.example.com/evil.js",
"violated-directive": "script-src 'self' https://apis.google.com",
"original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser"
}
}
Отчет содержит полезную информацию для поиска причины нарушения политики, включая страницу, на которой это произошло ( document-uri
), referrer
этой страницы , ресурс, нарушивший политику страницы ( blocked-uri
), конкретную директиву, которую он нарушил ( violated-directive
) и полную политику страницы ( original-policy
).
Только отчет
Если вы только начинаете работать с CSP, мы рекомендуем использовать режим только отчетов, чтобы оценить состояние вашего приложения, прежде чем менять политику. Для этого вместо отправки заголовка Content-Security-Policy
отправьте заголовок Content-Security-Policy-Report-Only
:
Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;
Политика, указанная в режиме только отчетов, не блокирует ограниченные ресурсы, но отправляет отчеты о нарушениях в указанное вами место. Вы даже можете отправить оба заголовка, чтобы обеспечить соблюдение одной политики и одновременно контролировать другую. Это отличный способ протестировать изменения в вашем CSP, одновременно применяя текущую политику: включите отчеты для новой политики, отслеживайте отчеты о нарушениях и исправляйте любые ошибки, а когда вы будете удовлетворены новой политикой, начните применять ее.
Использование в реальном мире
Первым шагом на пути к разработке политики для вашего приложения является оценка загружаемых им ресурсов. Когда вы поймете структуру своего приложения, создайте политику, основанную на его требованиях. В следующих разделах рассматриваются несколько распространенных случаев использования и процесс принятия решений по их поддержке в соответствии с рекомендациями CSP.
Виджеты социальных сетей
- Кнопка «Мне нравится» в Facebook имеет несколько вариантов реализации. Мы рекомендуем использовать версию
<iframe>
, чтобы изолировать ее от остальной части вашего сайта. Для правильной работы требуется директиваchild-src https://facebook.com
. - Кнопка «Твитнуть» X зависит от доступа к сценарию. Переместите предоставленный им скрипт во внешний файл JavaScript и используйте директиву
script-src https://platform.twitter.com; child-src https://platform.twitter.com
. - Другие платформы имеют аналогичные требования и могут быть решены аналогичным образом. Чтобы протестировать эти ресурсы, мы рекомендуем установить для параметра
default-src
значение'none'
и наблюдать за консолью, чтобы определить, какие ресурсы вам нужно включить.
Чтобы использовать несколько виджетов, объедините директивы следующим образом:
script-src https://apis.google.com https://platform.twitter.com; child-src https://plusone.google.com https://facebook.com https://platform.twitter.com
Карантин
Для некоторых веб-сайтов вам необходимо убедиться, что можно загружать только локальные ресурсы. В следующем примере разрабатывается CSP для банковского сайта, начиная с политики по умолчанию, которая блокирует все ( default-src 'none'
).
Сайт загружает все изображения, стили и скрипты из CDN по адресу https://cdn.mybank.net
и подключается к https://api.mybank.com/
используя XHR для получения данных. Он использует фреймы, но только для страниц, локальных для сайта (без сторонних источников). На сайте нет Flash, нет шрифтов, нет дополнений. Самый ограничительный заголовок CSP, который он может отправить, таков:
Content-Security-Policy: default-src 'none'; script-src https://cdn.mybank.net; style-src https://cdn.mybank.net; img-src https://cdn.mybank.net; connect-src https://api.mybank.com; child-src 'self'
только SSL
Ниже приведен пример CSP для администратора форума, который хочет гарантировать, что все ресурсы на его форуме загружаются только по защищенным каналам, но не имеет опыта в кодировании и не имеет ресурсов для переписывания стороннего программного обеспечения для форумов, полного встроенных скриптов. и стили:
Content-Security-Policy: default-src https:; script-src https: 'unsafe-inline'; style-src https: 'unsafe-inline'
Хотя https:
указан в default-src
, директивы сценария и стиля не наследуют этот источник автоматически. Каждая директива перезаписывает значение по умолчанию для этого конкретного типа ресурса.
Разработка стандарта CSP
Политика безопасности контента уровня 2 — это стандарт, рекомендуемый W3C. Рабочая группа W3C по безопасности веб-приложений разрабатывает следующую версию спецификации — Content Security Policy Level 3 .
Чтобы следить за обсуждением предстоящих функций, обратитесь к архивам списка рассылки public-webappsec@ .