- Улучшение сетевой безопасности с помощью Content Security Policy
- Принцип действия
- Поддержка браузерами
- Получение отчётов о нарушениях CSP
- Content-Security-Policy-Report-Only
- Прописывание заголовка
- CSP в дикой природе
- CSP Level 2
- Как настроить Content Security Policy (CSP)
- Как отсутствие CSP может навредить сайту?
- Содержание Content Security Policy
- Устанавливаем Content Security Policy на сайт
- Обработка отчётов
- Полезные материалы по Content Security Policy
Улучшение сетевой безопасности с помощью 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 :
Если вам нужно указать хосты для загрузки данных, то нужно указать ключевое слово 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:
Это означает, что браузер может загружать ресурсы только с нашего собственного домена. Но нам нужно использовать сервис Google Analytics, который будет пытаться скачивать JavaScript с www.google-analytics.com. А это уже нарушение нашей CSP. В этом случае report-uri отправит запрос со следующим JSON:
Content-Security-Policy-Report-Only
Если вы пока не уверены, стоит ли внедрять у себя CSP, то можно попробовать вместо заголовка Content-Security-Policy использовать Content-Security-Policy-Report-Only . В этом случае CSP будет регистрировать нарушения без какого-либо блокирования ресурсов. Можно даже использовать одновременно Content-Security-Policy и Content-Security-Policy-Report-Only , обкатывая на второй те или иные конфигурации.
Прописывание заголовка
HTTP-заголовок можно прописать прямо в конфигурационных файлах на сервере:
Также многие языки программирования и фреймворки позволяют добавлять заголовки программно (например, PHP, Node.js):
CSP в дикой природе
Давайте посмотрим, как CSP внедрён в Facebook:
Обратите внимание на использование символов подстановки для описания поддоменов, а также номеров портов в connect-src .
А теперь вариант Twitter:
Здесь везде прописаны 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-заголовок:
и в тэг инлайн-скрипта:
Если вы захотите использовать хэши, то сначала их нужно сгенерировать на сервере и включить в заголовок CSP, соответственно в директивы style-src или script-src . Перед рендерингом страницы браузер вычисляет хэш скрипта/стиля, и если он совпадает с указанным, то выполнение разрешается.
Пример хэша, сгенерированного из строковой console.log(‘Hello, SitePoint’); с помощью алгоритма Sha256 (base64).
Во время рендеринга страницы браузер для каждого инлайн-скрипта вычисляет хэши и сравнивает с перечисленными в CSP. Приведённый выше хэш позволяет выполнить скрипт:
Источник
Как настроить Content Security Policy (CSP)
Три года назад организацией Mozilla Foundation был разработан новый стандарт политики безопасности, который предотвращает XSS-атаки и другие, связанные с ним виды атак запрещая подгружать и выполнять скрипты с запрещённых ресурсов. Называется он Content Security Policy (CSP), что в переводе означает «Политика безопасности контента».
На момент написания статьи стандарт CSP находится в статусе Candidate Recommendation, что означает возможное принятие этого стандарта в будущем W3C консорциумом. На данный момент все популярные браузеры поддерживают этот стандарт.
Браузер | Версия | Примечания |
---|---|---|
Chrome | 25+ | Полная поддержка |
Firefox | 23+ | |
Opera | 15+ | |
Яндекс.Браузер | ||
Firefox | 4-22 | Поддерживают нестандартный заголовок X-Content-Security-Policy и частично поддерживают стандартный |
IE | 10+ | |
Chrome | 14-24+ | Поддерживают нестандартный заголовок X-Webkit-CSP и частично поддерживают стандартный |
Safari | 5-7 |
Как отсутствие CSP может навредить сайту?
Допустим у вас есть сайт, на котором вы показываете рекламу пользователям и честно зарабатываете деньги. И всё идёт хорошо, пока к вам не начнут ходит пользователи с заражёнными браузерами. Заражённый браузер будет подменять рекламу на вашем сайте на свою и показывать её пользователю. Как следствие — пессимизация со стороны поисковиков и падение дохода. Если же вы введёте политику CSP на своём сайте, то чужая реклама уже не покажется конечному пользователю, потому что сервер с которого реклама будет пытаться загрузиться находится не в белом списке, впрочем обо всё по порядку.
Содержание Content Security Policy
По сути Content Security Policy — это заголовок, который сервер отправляет браузеру. Давайте разберём более детально из чего же он состоит.
Директива | Назначение |
---|---|
default-src | В этой директиве задаются белые списки хостов, которые будут автоматически присвоены не заданным директивам. |
script-srс | Белый список хостов с которых разрешается загрузка javascript |
style-src | Белый список хостов с которых разрешается загрузка css |
object-src | Белый список хостов с которых разрешается загрузка Flash-подобных плагинов |
img-src | Белый список хостов с которых разрешается загрузка картинок |
media-src | Белый список хостов с которых разрешается загрузка аудио и видео |
frame-src | Белый список хостов с которых разрешается загрузка iframe’ов |
font-src | Белый список хостов с которых разрешается загрузка шрифтов |
connect-src | Специальные директивы для XMLHttpRequest, WebSocket и EventSource. Обратите внимание, что для каждой из этих директив задаётся список не урлов, а хостов, с которыми разрешено общаться браузеру. |
report-uri | Url, на который будет отсылаться JSON-отчёт о нарушениях политики. Пример отчёта будет показан ниже в статье. |
Сейчас ещё немного теории, и потом сразу перейдём к практики, потерпите 😉
Ключевое слово | Назначение |
---|---|
‘self’ | Определяет текущий хост. |
‘none’ | Запрещает всё. «You shall not pass!» 🙂 |
‘unsafe-inline’ | Используется только в директивах script-src и style-src . Разрешает выполнять inline-скрипты на странице. Не рекомендую использвать это ключевое слово, т.к. это развязывает руки злоумышленнику и даёт право исполнять любые инлайновые скрипты на странице. Простыми словами, это дыра в безопасности. |
‘unsafe-eval’ | Используется только в script-src и разрешает кодогенерацию, например: eval, new Function, setTimeout(‘var foo = «bar» ‘, 7) |
Устанавливаем Content Security Policy на сайт
Как я уже писал выше, CSP — это обычный http заголовок, который можно наблюдать в консоли Google Chrome, наряду с остальными заголовками:
Чтобы лучше понять как работает Content Security Policy, давайте немного поэкспериментируем. Создайте файл index.php и напишите в него следующий код:
Обратите внимание, что в http заголовке я указал Content-Security-Policy-Report-Only он аналогичен Content-Security-Policy, с той лишь разницей, что не блокирует ресурсы, а только оповещает о нарушении. Крайне полезная штука при тестировании системы перед внедрением!
Давайте разберёмся, что же мы понаписали. Первым делом мы указали в http заголовке директиву default-src ‘self’ , что означает что подгружать ресурсы можно только со своего хоста. Любые инлайн скрипты и css запрещены. Ок, идём дальше и видим:
Т.е. попробуем выполнить инлайн скрипт и загрузить картинку со стороннего хоста. И посмотрим как отреагирует наш бравый защитник:
CSP отреагировал адекватно. Т.е. подгрузил картинку и выполнил инлайновый javascript, но при этом сказал нам в консоли «ата-та!», а именно: сообщил о том, что произошло два нарушения.
Теперь давайте изменим заголовок с Content-Security-Policy-Report-Only на Content-Security-Policy и посмотрим что будет:
Пендальф CSP никого не пустил.
Инлайн скрипт не был выполнен, а картинка не загрузилась. Круто, правда?
Теперь можете поэкспериментировать самостоятельно. Вам пригодятся две таблички выше в статье, в которых мы рассмотрели директивы и ключевые слова для указания хостов. Попробуйте заменить ‘self’ на https://zabolotskikh.com/ и посмотрите что произойдет — картинка сможет загрузиться, так как её сервер был указан в белом списке.
Хочу обратить ваше внимание, что хост желательно указывать с протоколом, так как в противном случае протокол будет взят из текущего хоста. Например, если вы укажите хост как zabolotskikh.com , а ваш сервер работает по протоколу https, то в белом списке окажется https://zabolotskikh.com/ .
Обработка отчётов
Вся прелесть этой политики в том, что помимо блокирования, мы также можем собирать отчёты о нарушениях! Помните в примере в http заголовке мы указали url report-uri http://localhost/csp/collector.php для сбрасывания отчётов? Как не сложно догадаться на этот url будут отправляться все отчёты о нарушениях.
Вот так выглядит отчёт о нарушении (в формате JSON):
С этим отчётом вы можете делать всё что угодно, например сохранять в базу, отправлять на почту. Я предлагаю записывать все нарушения в csv файл. Давайте сделаем это!
Создайте файл collector.php и напишите в него следующие строки:
Теперь ещё раз обновите страницу и посмотрите в директорию http://localhost/csp/. У вас должен появиться файл report.csv с двумя строчками кода:
Ура! Мы поймали отчёт о нарушениях и записали его в файл. Можете показать этот файл друзьям 😉 А лучше всего начните внедрять CSP на свой сайт, сначала в режиме тестирования, а потом в «боевом» виде. На этапе тестирования отчёт поможет вам анализировать какие директивы реагируют на нарушения и соответствующим образом настраивать их.
Полезные материалы по Content Security Policy
- http://w3c.github.io/webappsec-csp/ — спецификация стандарта.
- https://mathiasbynens.be/notes/csp-reports — интересная статья на английском про CSP.
- https://github.com/immrr/csp-report — скрипт для обработки отчётов о нарушениях и записи их в базу.
- http://www.cspplayground.com/csp_validator — валидатор CSP http заголовка! Очень полезная штуковина, вы можете проверить свой заголовок на корректность, согласно спецификациям стандарта.
Здравствуй дорогой читатель! Я рад приветствовать тебя на страницах моего блога. Уже несколько лет я занимаюсь веб-программированием и рад поделиться с тобой своими знаниями и советами. Если тебе понравились мои статьи, ты можешь подписаться на рассылку блога, из неё ты узнаешь много интересного!
Источник