Компания
89,49
рейтинг
25 ноября 2015 в 10:15

Разработка → Улучшение сетевой безопасности с помощью Content Security Policy перевод



Content Security Policy (CSP, политика защиты контента) — это механизм обеспечения безопасности, с помощью которого можно защищаться от атак с внедрением контента, например, межсайтового скриптинга (XSS, 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. Также это ключевое слово разрешает инлайн-атрибуты CSS style, инлайн-обработчики событий (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. Приведённый выше хэш позволяет выполнить скрипт:

< sсript >console.log('Hello, SitePoint');< /sсript >

Обратите внимание, что пробелы имеют значение, так что эти скрипты выполнены не будут:

<sсript> console.log('Hello, SitePoint');</sсript>

<sсript>console.log('Hello, SitePoint'); </sсript>

В заключение


Если у вас есть опыт использования CSP, — положительный или отрицательный, — поделитесь, пожалуйста, в комментариях. Также было бы интересно узнать о нестандартных задачах, которые вы решали с помощью CSP.
Автор: @NIX_Solutions Ian Oxley
NIX Solutions
рейтинг 89,49

Комментарии (5)

  • +1
    Пробовали внедрять на своих проектах. В итоге по статистике собранных отчетов мы помогали 0.5% пользователей (т.е. блокировали действительно вредоносные расширения) и мешали примерно 30% пользователей, т.е. в зависимости от браузера (особенно этим грешит Firefox) при заявленной поддержке данной технологии что-нибудь идет не так и JavaScript (особенно inline) перестает работать. На мобильных платформах вообще практически не поддерживается.
  • 0
    Посмотрел помимо facebook и twitter еще несколько крупных проектов — везде используются unsafe-unline, unsafe-eval.
    Разве с такими директивами не получается защита чисто психологическая?
    Сам внедрял — но только для админ-панели. В этом случае, даже если в каком-то браузере сломается — не сильно критично, безопасность важнее.
  • 0
    Есть проблема с шаблонизаторами, т.к в них приходится использовать eval так или иначе. Что бы обезопасить себя — вынес небезопасный код в webworker-песочницу с отдельной политикой безопасности(можно только исполнятся и ничего более, на выходе — строка с готовым шаблоном, доступ имеет только к минимально-необходимой части данных).

    Столкнулся с проблемой в Firefox — он не понимает отдельную политику для WebWorker(как это делает, к примеру, Chrome). Хотя согласно спеке — должен. Кому интересно — сюда, особенно неплохо будет если за баг проголосуют что бы хоть какие-то подвижки были, а то у Firefox всё плохо с рассмотрением багов.

    А так — CSP это путь в безопасное будущее веба, если будет работать согласно спецификации. Пока что, к сожалению — только простейшие случаи. А то unsafe-inline или unsafe-eval директивы ломают весь смысл использования CSP.
    PS ведётся разработка CSP 3.0. Я вот например подметил что нет возможности задать nonce для веб-воркера, хотя для всех остальных скриптов это можно сделать.
  • 0
    Спасибо за статью. Как раз сейчас заинтересовался этим вопросом
    • 0
      Всегда рады :)

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

Самое читаемое Разработка