Использование политики безопасности контента (CSP) для защиты веб-приложений
Перевод статьи: Zbigniew Banach — Using Content Security Policy (CSP) to Secure Web Applications
Политика безопасности контента (Content Security Policy — CSP) — это стандарт компьютерной безопасности, обеспечивающий дополнительный уровень защиты от межсайтовых сценариев (XSS), clickjacking (перехвата кликов) и других атак с использованием кода, основанных на выполнении вредоносного контента в контексте доверенной веб-страницы. Используя подходящие директивы CSP в заголовках ответа HTTP, вы можете выборочно указать, какие источники данных должны быть разрешены в вашем веб-приложении. В этой статье описано, как использовать заголовки CSP для защиты сайтов от атак XSS и других попыток обойти политику CSP.
Зачем нам нужен CSP?
Веб-безопасность основана на same-origin policy (SOP), которая запрещает веб-сайту доступ к данным за пределами собственного происхождения. Теоретически этого должно быть достаточно для обеспечения безопасности, но современный интернет требует, чтобы сайты включали множество ресурсов из внешних источников, таких как скрипты и другие ресурсы из сетей доставки контента (CDN), скрипты Google Analytics, шрифты, стили, модули комментариев , кнопки социальных сетей и т.д..
В то же время злоумышленники используют атаки с использованием cross-site scripting (XSS), чтобы обманным путем заставить веб-сайты, которым доверяет пользователь, распространять вредоносный код. Без дополнительных мер безопасности браузер выполнит весь код из доверенного источника и так как он не может определить, какой код является допустимым, то любой внедренный вредоносный код также выполнится.
Content Security Policy (CSP) — стандартизированный набор директив, которые сообщают браузеру, каким источникам контента можно доверять, а какие следует блокировать. Используя тщательно определенные политики, вы можете ограничить содержимое браузера, чтобы устранить многие распространенные векторы атак и значительно снизить риск атаки XSS. По умолчанию CSP также применяет современные стили кодирования сценариев для дополнительной безопасности.
Истории и поддержка браузеров
Content Security Policy — это рекомендация рабочей группы W3C по безопасности веб-приложений. Версия 1 (или уровень 1) была предложена в 2012 году, Level 2 — в 2014 году, а Level 3 разрабатывается с 2015 года в качестве проекта. Хотя это всего лишь рекомендации, создатели браузеров быстро внедрили CSP, начиная с Firefox 4. Подавляющее большинство современных браузеров поддерживают все или почти все директивы уровня 2, и в этой статье описывается уровень 2 CSP как фактический текущий стандарт.
Реализации CSP использовали 3 разных имени заголовка политики безопасности контента, в зависимости от браузера и времени принятия:
Content-Security-Policy
: Стандартное имя заголовка, рекомендованное W3C и используемое всеми современными реализациями (GoogleChrome начиная с версии 25, Firefox начиная с версии 23, Safari и другие браузеры на основе WebKit начиная с версии 528 WebKit). В настоящее время это единственный заголовок для использования.X-WebKit-CSP
(устарел): Экспериментальный заголовок, используемый в прошлом Chrome и другими браузерами на основе WebKit.X-Content-Security-Policy
(устарел): Экспериментальный заголовок, используемый в прошлом браузерами на базе Gecko 2.
Использование директив CSP
CSP позволяет определять различные ограничения содержимого с помощью директив, обычно указанных в заголовках ответа HTTP. Вот пример добавления заголовков CSP на веб-сервер Apache:
Header set Content-Security-Policy "default-src 'self';"
Добавленный в файл httpd.conf или .htaccess, он установит политику по умолчанию, разрешающую только контент из текущего источника (подробности см. ниже).
При необходимости вы также можете указать желаемые директивы на уровне страницы, используя метатеги HTML. Вот пример, который устанавливает ту же политику, что и выше:
<meta http-equiv="Content-Security-Policy" content="default-src
'self'">
Каждая директива состоит из имени, за которым следует одно или несколько значений, и заканчивается точкой с запятой. Вы можете использовать подстановочный знак * для обобщения целых значений, поддоменов, схем и портов:
Content-Security-Policy: default-src *://*.example.com
Этот заголовок позволяет использовать источники из любого субдомена example.com (но не самого example.com) по любой схеме (http, https и т.д.).
Официальная рекомендация W3C содержит полный список директив с более формальными определениями, но следующий обзор должен дать вам хорошее представление о наиболее часто используемых.
Директивы белого списка источников
Основной целью CSP является ограничение источников веб-контента, поэтому существует множество директив для указания разрешенных источников для различных типов ресурсов. Как только заголовок Content-Security-Policy указан, браузер будет отклонять любой контент из источников, которые явно не занесены в белый список, с использованием любой из приведенных ниже директив. Исходные значения разделены пробелами и могут включать как URL-адреса, так и специальные ключевые слова ‘none’, ‘self’, ‘unsafe-inline’ и ‘unsafe-eval’ (подробно обсуждается ниже). Обратите внимание, что каждая директива может быть указана только один раз в том же заголовке, а ключевые слова должны быть введены в одинарные кавычки.
default-src
является резервной директивой, используемой для определения политики содержимого по умолчанию для большинства исходных директив. Обычно используются default-src ‘self’, чтобы разрешить контент из текущего источника (но не его поддоменов), и default-src ‘none’, чтобы заблокировать все, что явно не занесено в белый список.script-src
используется для внесения в белый список источников скриптов. Чтобы разрешить скрипты только из текущего источника, используйте script-src ‘self’.style-src
используется для внесения в белый список источников стилей CSS. Чтобы разрешить таблицы стилей только из текущего источника, используйте style-src ‘self’.connect-src
указывает разрешенные источники для прямых подключений JavaScript, которые используют объекты EventSource, WebSocket или XMLHttpRequest.object-src
позволяет контролировать источники плагинов, таких как Flash. Обратите внимание, что вы также можете указать разрешенные типы плагинов, используя директиву plugin-types (не поддерживается в Firefox начиная с v76).img-src
позволяет ограничить источники изображения.font-src
указывает разрешенные источники для загрузки шрифтов.media-src
ограничивает источники для загрузки звуковых и видео ресурсов.child-src
используется для ограничения разрешенных URL-адресов для worker JavaScript и содержимого встроенных фреймов, включая встроенные видеоролики. На Level 3 вместо этого можно использовать директивы frame-src и worker-src для управления встроенным контентом и рабочими процессами соответственно.frame-ancestors
ограничивает URL-адреса, которые могут встраивать текущий ресурс в <iframe> , <object> и подобные элементы.
Написание JavaScript и CSS с CSP
Встроенный код является основным вектором нападения, потому что он всегда выполняется в текущем контексте, поэтому его нельзя ограничивать. Когда CSP включен, он блокирует весь встроенный код по умолчанию. Это означает, что никаких встроенных стилей или встроенных сценариев не должно существовать, включая встроенные обработчики событий или javascript: URL поэтому любой новый код должен следовать передовым методам использования исключительно внешних файлов сценариев и стилей. Ключевое слово unsafe-inline разрешает встроенный код для всех или некоторых источников скриптов, но W3C рекомендует избегать его, где это возможно.
Например, страница HTML и JavaScript старого стиля может содержать код сценария как в тегах <script> , так и в обработчиках встроенных событий:
<script>function performButtonAction() {
alert('You clicked the button!');}
</script>
<button onclick='performButtonAction();'>
Want to click the button?
</button>
Чтобы преобразовать это в современный и совместимый с CSP стиль, поместите сценарий в отдельный файл и используйте HTML чисто декларативно, что-то вроде:
<!-- buttonexample.html -->
<script src='buttonexample.js'></script>
<button id='examplebutton'>Want to click the button?</button>
и
// buttonexample.js
function performButtonAction() {
alert('You clicked the button!');
}
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('examplebutton')
.addEventListener('click', performButtonAction);
});
Даже после того, как типичные векторы атак были заблокированы, злоумышленники все равно могут выполнить сценарий, если используется динамически запускаемый (evaluated) код. Вот почему CSP также блокирует все функции запуска строк по умолчанию, включая eval (), new Function (), setTimeout ([string]) и аналогичные конструкции. Это приводит к нескольким изменениям в практике кодирования, включая использование JSON.parse () вместо eval() для анализа данных JSON.
Подобно unsafe-inline, вы можете использовать ключевое слово unsafe-eval, чтобы разрешить запуск кода для некоторых или всех источников. Опять же, это противоречит лучшей практике для современного кода и должно использоваться только для устаревшего кода, который не может быть реорганизован.
Nonces и Hashes чтобы разрешить встроенные скрипты
Если вам абсолютно необходимо сделать исключение для некоторого унаследованного встроенного кода, который нельзя перенести в отдельный файл, CSP предоставляет две функции, позволяющие разрешать определенные блоки кода, не прибегая к unsafe-inline. Для внесения в белый список определенного фрагмента кода вы можете использовать nonce (одноразовый) номер (уникальный одноразовый идентификатор) для тега <script> или криптографический хеш для самого кода. Затем вы можете указать nonce или hash в вашей директиве script-src, чтобы разрешить этот фрагмент встроенного кода.</p>
По определению, nonce номер — это «число, используемое один раз», поэтому оно должен генерироваться случайным образом для каждой загрузки страницы. Чтобы использовать nonce номер сценария, укажите его в теге <script> с помощью атрибута nonce, а также добавьте его в директиву script-src, добавив к значению nonce-. Например, у вас может быть устаревший код:
<script nonce="uG2bsk6JIH923nsvp01n24KE">
alert('Hello from Netsparker');
</script>
Чтобы разрешить этот конкретный тег сценария, используйте:
Content-Security-Policy: script-src 'nonce-uG2bsk6JIH923nsvp01n24KE'
Генерация nonce номера для каждой загрузки страницы может быть проблематичным, поэтому другой подход заключается в использовании криптографического хэша самого разрешенного кода. Для этого начните с вычисления хэша SHA для всех символов внутри тега <script> (но без самого тега). Затем вы можете указать значение хеша в директиве script-src, добавив к нему префикс sha256-, sha384- или sha512-, в зависимости от используемого алгоритма. Вот тот же пример, но без атрибута nonce:
<script>alert('Hello from Netsparker');</script>
Тогда директива, использующая хэш SHA256 этого кода:
Content-Security-Policy: script-src 'sha256-db9763638d4e260082532ed81baf740fe3589b0920ce6039233435abfdbc9ef7'
Директивы CSP на уровне страницы
Помимо источников контента, внесенных в белый список, CSP также может налагать ограничения на действия, которые может выполнять текущая страница. Чтобы использовать эту функцию, используйте директиву sandbox, чтобы обрабатывать страницу так, как если бы она находилась внутри изолированного фрейма. Полное описание ограничений, накладываемых песочницей на страницу, см. в разделе Sandboxing в спецификации HTML5.
Чтобы повысить безопасность старых сайтов с большим количеством устаревших HTTP-страниц, вы можете использовать директиву upgrade-insecure-requests для перезаписи небезопасных URL-адресов. Это дает указание агентам пользователя изменять HTTP на HTTPS в схемах URL-адресов и может иметь неоценимое значение, если у вас все еще есть несколько URL-адресов HTTP.
Политика тестирования и мониторинг нарушений
Политика безопасности контента предоставляет мощные функциональные возможности для контроля источников контента и поведения страниц. Однако это также означает, что одна неправильно настроенная директива может сделать сайт частично или полностью недоступным для посетителей, поэтому вам нужен способ безопасного тестирования директив.
Прежде чем начать работу с директивами CSP, вы можете использовать заголовок Content-Security-Policy-Report-Only вместо Content-Security-Policy. В режиме report-only браузер будет отслеживать политику и сообщать о нарушениях, но фактически не применяет ограничения. Используйте директиву report-uri, чтобы сообщить браузеру, где он должен публиковать отчеты о нарушениях в формате JSON. Это может быть любой локальный или внешний URI, например:
Content-Security-Policy-Report-Only: default-src 'self'; ...;
report-uri /your_csp_report_parser;
Обратите внимание, что вы можете комбинировать заголовки Content-Security-Policy-Report-Only и Content-Security-Policy, чтобы протестировать новую политику, при этом применяя существующую.
После того, как политика активна, вы можете использовать ту же директиву report-uri для получения подробных отчетов о нарушениях политики. Каждый отчет JSON начинается с атрибута csp-report и выглядит примерно так:
{
"csp-report": {
"document-uri": "http://netsparker.com/index.html",
"referrer": "http://nasty.example.com/",
"blocked-uri": "http://nasty.example.com/nasty.js",
"violated-directive": "script-src 'self' https://apis.google.com",
"original-policy": "script-src 'self' https://apis.google.com;
report-uri http://netsparker.com/your_csp_report_parser"
}
}
Как видите, отчеты предоставляют информацию о каждом нарушении политики, включая заблокированный URI и нарушенную директиву. Это значительно упрощает устранение неполадок, особенно для политик с сотнями директив и значений.
Поддержка Content Security Policy в Netsparker
В течение последних нескольких лет CSP Level 2 был реализован во всех современных браузерах и широко используется в Интернете как эффективный способ снижения риска XSS. Чтобы отразить это, Netsparker проверяет наличие HTTP-заголовков Content-Security-Policy и сообщает об уязвимости «Best Practice», если они отсутствуют.
Однако простого наличия заголовка CSP недостаточно, поскольку недопустимые директивы будут игнорироваться браузерами (и, следовательно, неэффективны), в то время как небезопасные значения директив не обеспечат ожидаемый уровень защиты. Netsparker выполняет более 20 подробных проверок, чтобы убедиться, что директивы используют правильный синтаксис в сочетании со значениями, обеспечивающими эффективную безопасность. См. Vulnerability Index для получения полного списка проверок CSP, доступных в Netsparker.
Демонстрация Content Security Policy в действии
И, наконец, для практической демонстрации настройки заголовков CSP посмотрите это еженедельное интервью по безопасности с исследователем безопасности Netsparker Свеном Моргенротом. Разговаривая с Полом Асадуряном, Свен рассказывает о проблемах, которые CSP предназначен для решения, и показывает практическую демонстрацию заголовков CSP в действии.
Вы выбрали для перевода плохой источник информации.
правильный хэш будет ‘sha256-25djY41OJgCCUy7YG690D+NYmwkgzmA5IzQ1q/28nvc=’, а не ‘sha256-db9763638d4e260082532ed81baf740fe3589b0920ce6039233435abfdbc9ef7’. Автор исходного источника «забыл» base64-кодировать его, а не кодированный он не работает.
неправильный и работать не будет. Потому, что специальный символ * не разрешён в схеме.
и, пожалуйста, исправьте опечатку в:
upgrade-insecure-reports -> upgrade-insecure-requests
Спасибо, за подробный комментарий