Политика безопасности контента

Политика безопасности контента может значительно снизить риск и влияние атак с использованием межсайтовых сценариев в современных браузерах.

Поддержка браузера

  • 25
  • 14
  • 23
  • 7

Источник

Модель безопасности Интернета основана на политике единого происхождения . Например, код 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, а также из источника текущей страницы, но не из какого-либо другого источника. Если злоумышленник внедряет код на ваш сайт, браузер выдает ошибку и не запускает внедренный скрипт.

Ошибка консоли: отказался загружать сценарий «http://evil.example.com/evil.js», поскольку он нарушает следующую директиву политики безопасности контента: script-src «self» https://apis.google.com
Консоль отображает ошибку, когда сценарий пытается запуститься из источника, не входящего в список разрешенных.

Политика применяется к широкому спектру ресурсов

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@ .