Статьи

Улучшение веб-безопасности с помощью политики безопасности контента

Политика безопасности контента (CSP) — это механизм безопасности, который помогает защитить от атак внедрения контента, таких как межсайтовый скриптинг (XSS). Это декларативная политика, которая позволяет предоставить браузеру белый список того, откуда он может загружать ресурсы, может ли браузер использовать встроенные стили или сценарии, и может ли он использовать динамическую оценку JavaScript — например, с помощью eval . Если есть попытка загрузить ресурс из того места, которого нет в этом белом списке, загрузка этого ресурса блокируется.

Как это устроено

CSP в настоящее время является рекомендацией кандидата, опубликованной рабочей группой W3C по безопасности веб-приложений . Он доставляется в браузер через HTTP-заголовок Content-Security-Policy , который содержит одну или несколько директив, которые входят в белый список доменов, из которых браузеру разрешено загружать ресурсы. CSP 1.0 имеет следующие директивы:

  • default-src
  • script-src
  • object-src
  • style-src
  • img-src
  • media-src
  • frame-src
  • font-src
  • connect-src

default-src , как следует из названия, устанавливает исходный список по умолчанию для оставшихся директив. Если директива явно не включена в заголовок CSP, она вернется к использованию значений из списка default-src .

Все директивы следуют той же схеме:

  • self используется для ссылки на текущий домен
  • один или несколько URL могут быть указаны в списке через пробел
  • none указывает, что ничего не должно быть загружено для данной директивы, например, object-src 'none' указывает, что никакие плагины, такие как Flash или Java, не должны загружаться.

В простейшем случае мы могли бы определить CSP для загрузки ресурсов только из текущего домена следующим образом:

 Content-Security-Policy: default-src 'self'; 

Если предпринята попытка загрузить ресурс из любого другого домена, он блокируется браузером, и на консоль записывается сообщение:

Консоль браузера в Chrome

По умолчанию CSP также ограничивает выполнение JavaScript, запрещая встроенные сценарии и динамическую оценку кода. Это, в сочетании с белым списком, откуда можно загружать ресурсы, имеет большое значение для предотвращения атак внедрения контента. Например, попытка атаки XSS внедрить тег встроенного сценария будет заблокирована:

Встроенный скрипт заблокирован в Chrome

Как и любая попытка загрузить внешний скрипт, который не был включен в CSP:

Внешний скрипт заблокирован в Chrome

Пути в настоящее время не поддерживаются в URL, поэтому вы не можете заблокировать свой сайт, чтобы обслуживать только CSS с http://cdn.example.com/css. Вам нужно только указать домен — например, http://cdn.example.com. Однако вы можете использовать подстановочные знаки, например, для указания всех поддоменов данного домена, например * .mycdn.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; 

Если вам нужно использовать URL-адреса data для загрузки ресурсов, вам необходимо включить data: в вашу директиву, например, img-src 'data:'; ,

Помимо перечисления доменов, две дополнительные функции, поддерживаемые script-src и style-src являются unsafe-inline и unsafe-eval :

  • unsafe-inline может использоваться style-src и script-src для указания того, что встроенные теги <style> и <script> разрешены. CSP использует политику согласия. То есть, если вы не включите unsafe-inline , тогда все встроенные теги <style> и <script> будут заблокированы. unsafe-inline также разрешает встроенные атрибуты style для CSS и разрешает встроенные обработчики событий (onclick, onmouseover и т. д.) и URL-адреса javascript: (например, <a href="javascript:foobar()"> ).
  • unsafe-eval может использоваться script-src . Опять же, он использует политику opt-in, поэтому, если ваш script-src явно не включает unsafe-eval , какую-либо динамическую оценку кода, которая включает использование eval , конструктор Function и передачу строк в setTimeout и setInterval это setInterval заблокирован.

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

Браузерная поддержка CSP 1.0 довольно хороша, с Internet Explorer, являющимся обычным слоном в комнате: IE10 и IE11 частично поддерживают CSP через заголовок X-Content-Security-Policy , но даже тогда они, кажется, поддерживают только дополнительную sandbox директива

Захват нарушений CSP с помощью report-uri

Ранее я упоминал, что любое нарушение вашего CSP будет регистрироваться в консоли браузера. Хотя это может быть хорошо, когда ваш сайт находится в стадии разработки, это не очень удобно, когда вы развертываете CSP в рабочей среде.

Вместо этого вы можете использовать report-uri для регистрации всех нарушений CSP. Эта директива принимает URL-адрес в качестве значения и отправляет HTTP-запрос POST на этот URL-адрес при обнаружении нарушения CSP. Тело запроса содержит объект JSON, заполненный сведениями о нарушении.

Чтобы проиллюстрировать это, предположим, что у нас есть CSP следующим образом:

 Content-Security-Policy: default-src 'self'; report-uri: https://example.com/csp/report; 

Это означает, что браузеру разрешено загружать ресурсы только из нашего собственного домена. Тем не менее, наш сайт использует Google Analytics, поэтому он пытается загрузить JavaScript из
www.google-analytics.com. Это нарушает наш CSP, поэтому следующий JSON отправляется через HTTP-запрос POST в нашу report-uri :

 { "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, вы можете использовать CSP для пробного запуска, используя HTTP-заголовок Content-Security-Policy-Report-Only вместо Content-Security-Policy . Это работает так же, как и заголовок CSP, но он сообщает только о нарушениях, фактически не применяя политику, блокируя ограниченные ресурсы. Вы даже можете использовать оба заголовка одновременно, применяя одну политику, отслеживая влияние любых изменений в другой.

Настройка заголовка CSP HTTP

Ранее я упоминал, что CSP отправляется заголовок HTTP. Задать заголовки 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';"; 

В качестве альтернативы многие языки программирования / фреймворки поддерживают добавление заголовков HTTP программным способом, например заголовок PHP или setHeader узла, поэтому вы можете использовать их для установки заголовка CSP:

 # PHP example header("Content-Security-Policy: default-src 'self'"); # Node.js example request.setHeader("Content-Security-Policy", "default-src 'self'"); 

CSP в дикой природе

Давайте посмотрим, как Facebook и Twitter реализуют 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; 

Обратите внимание, как Facebook использует подстановочные знаки для обоих поддоменов, а также номера портов в connect-src .

Вот CSP, используемый 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

Уровень 2 CSP в настоящее время является Рекомендацией кандидата и определяет некоторые новые директивы и меры безопасности:

  • base-uri контролирует, разрешено ли документу манипулировать базовым URI страницы.
  • child-src заменяет frame-src .
  • form-action контролирует способность документа отправлять HTML-формы.
  • frame-ancestors работает как заголовок X-Frame-Options, управляя тем, как этот документ может быть встроен в другие документы, и фактически предназначен для замены этого заголовка.
  • plugin-types плагинов управляют тем, какие конкретные плагины могут быть загружены страницей, например Flash, Java, Silverlight и т. д.

JSON, отправленный в report-uri получает несколько дополнительных полей: effective-directive содержит имя директивы, которая была нарушена; и status-code , который содержит код состояния HTTP запрашиваемого ресурса, или ноль, если ресурс не был запрошен по HTTP.

CSP Level 2 также включает возможность защиты встроенных сценариев и таблиц стилей с помощью одноразовых номеров и хэшей.

Защита встроенных стилей и скриптов с использованием одноразового номера

Одноразовый номер — это просто случайная строка, которая генерируется на сервере, включается в заголовок CSP и также включается в тег встроенного сценария. Итак, наш CSP-заголовок с одноразовым номером будет выглядеть примерно так:

 Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-Xiojd98a8jd3s9kFiDi29Uijwdu'; 

При отображении страницы этот же одноразовый номер должен быть включен в атрибут nonce тега script для выполнения встроенного сценария:

 <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 соответственно. Затем браузер вычисляет хеш блока style / script перед тем, как визуализировать страницу. Если хеш, вычисленный браузером, совпадает с хешем, вычисленным на сервере, блок style / script разрешается выполнять.

Здесь заголовок CSP включает хэш-кодировку sha256-base64 строки console.log('Hello, SitePoint'); в директиве script-src :

 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> <script>console.log('Hello, World');</script> 

Вывод

В этой статье мы рассмотрели CSP 1.0 и то, как вы можете использовать его директивы для создания белого списка, откуда ваш сайт может загружать ресурсы. Мы рассмотрели, как вы можете использовать report-uri для сбора информации о запросах, которые нарушают ваш CSP, и как Facebook и Twitter используют CSP сегодня. В заключение мы рассмотрели некоторые изменения, которые появятся в CSP 2.0, в частности, как вы можете получить больше защиты для ваших встроенных стилей и сценариев, используя одноразовые номера и хэши.

Если у вас есть вопросы о CSP и как он работает, пожалуйста, прокомментируйте ниже. Вы уже попробовали CSP? Если так, как все прошло?