php content security policy header
Как настроить 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-отчёт о нарушениях политики. Пример отчёта будет показан ниже в статье. |
Сейчас ещё немного теории, и потом сразу перейдём к практики, потерпите 😉
Устанавливаем Content Security Policy на сайт
Как я уже писал выше, CSP — это обычный http заголовок, который можно наблюдать в консоли Google Chrome, наряду с остальными заголовками:
Чтобы лучше понять как работает Content Security Policy, давайте немного поэкспериментируем. Создайте файл index.php и напишите в него следующий код:
Обратите внимание, что в http заголовке я указал Content-Security-Policy-Report-Only он аналогичен Content-Security-Policy, с той лишь разницей, что не блокирует ресурсы, а только оповещает о нарушении. Крайне полезная штука при тестировании системы перед внедрением!
Т.е. попробуем выполнить инлайн скрипт и загрузить картинку со стороннего хоста. И посмотрим как отреагирует наш бравый защитник:
CSP отреагировал адекватно. Т.е. подгрузил картинку и выполнил инлайновый javascript, но при этом сказал нам в консоли «ата-та!», а именно: сообщил о том, что произошло два нарушения.
Теперь давайте изменим заголовок с Content-Security-Policy-Report-Only на Content-Security-Policy и посмотрим что будет:
Пендальф CSP никого не пустил.
Инлайн скрипт не был выполнен, а картинка не загрузилась. Круто, правда?
Теперь можете поэкспериментировать самостоятельно. Вам пригодятся две таблички выше в статье, в которых мы рассмотрели директивы и ключевые слова для указания хостов. Попробуйте заменить ‘self’ на 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
Здравствуй дорогой читатель! Я рад приветствовать тебя на страницах моего блога. Уже несколько лет я занимаюсь веб-программированием и рад поделиться с тобой своими знаниями и советами. Если тебе понравились мои статьи, ты можешь подписаться на рассылку блога, из неё ты узнаешь много интересного!
Content-Security-Policy
The HTTP Content-Security-Policy response header allows web site administrators to control resources the user agent is allowed to load for a given page. With a few exceptions, policies mostly involve specifying server origins and script endpoints. This helps guard against cross-site scripting attacks (Cross-site_scripting).
For more information, see the introductory article on Content Security Policy (CSP).
Header type | Response header |
---|---|
Forbidden header name | no |
Syntax
consists of: with no internal punctuation.
Directives
Fetch directives
Fetch directives control the locations from which certain resource types may be loaded.
Restricts the URLs which can be loaded using script interfaces
Serves as a fallback for the other fetch directives.
Specifies valid sources of images and favicons.
Specifies valid sources of application manifest files.
Note: Elements controlled by object-src are perhaps coincidentally considered legacy HTML elements and are not receiving new standardized features (such as the security attributes sandbox or allow for ). Therefore it is recommended to restrict this fetch-directive (e.g., explicitly set object-src ‘none’ if possible).
Specifies valid sources to be prefetched or prerendered.
Улучшение сетевой безопасности с помощью Content Security Policy
Content Security Policy (CSP, политика защиты контента) — это механизм обеспечения безопасности, с помощью которого можно защищаться от атак с внедрением контента, например, межсайтового скриптинга (XSS, cross site scripting). CSP описывает безопасные источники загрузки ресурсов, устанавливает правила использования встроенных стилей, скриптов, а также динамической оценки JavaScript — например, с помощью eval. Загрузка с ресурсов, не входящих в «белый список», блокируется.
Принцип действия
CSP сейчас является кандидатом в рекомендации консорциума W3C. Для использования политики страница должна содержать HTTP-заголовок Content-Security-Policy с одной и более директивами, которые представляют собой «белые списки». В версии 1.0 поддерживаются следующие директивы:
Для всех директив действуют следующие правила:
Content-Security-Policy: default-src ‘self’;
Попытка загрузки ресурсов из иных доменов будут пресекаться браузером с выдачей уведомления в консоли:
По умолчанию CSP ограничивает исполнение JavaScript путём запрета встроенных скриптов и динамической оценки кода. В комбинации с «белыми списками» это позволяет предотвращать атаки с внедрением контента. Например, XSS-атаку с внедрением тэга инлайн-скрипта:
Загрузка внешних скриптов, которые не включены в CSP, также будет пресечена:
На данный момент в перечне URL нельзя прописывать пути ( http://cdn.example.com/path ), можно лишь перечислять сами домены ( http://cdn.example.com ). Зато поддерживаются символы подстановки, так что вы можете описать сразу все поддомены ( http://*.example.com ).
Поддержка браузерами
Получение отчётов о нарушениях CSP
Допустим, у нас есть такая CSP:
Это означает, что браузер может загружать ресурсы только с нашего собственного домена. Но нам нужно использовать сервис Google Analytics, который будет пытаться скачивать JavaScript с www.google-analytics.com. А это уже нарушение нашей CSP. В этом случае report-uri отправит запрос со следующим JSON:
Content-Security-Policy-Report-Only
Прописывание заголовка
HTTP-заголовок можно прописать прямо в конфигурационных файлах на сервере:
Также многие языки программирования и фреймворки позволяют добавлять заголовки программно (например, PHP, Node.js):
CSP в дикой природе
Давайте посмотрим, как CSP внедрён в Facebook:
А теперь вариант Twitter:
CSP Level 2
Также является кандидатом в рекомендации. В CSP Level 2 сделаны следующий нововведения:
Nonce — это генерируемая случайным образом на сервере строковая переменная. Она добавляется в CSP-заголовок:
и в тэг инлайн-скрипта:
Пример хэша, сгенерированного из строковой console.log(‘Hello, SitePoint’); с помощью алгоритма Sha256 (base64).
Во время рендеринга страницы браузер для каждого инлайн-скрипта вычисляет хэши и сравнивает с перечисленными в CSP. Приведённый выше хэш позволяет выполнить скрипт:
Полное руководство по настройке HTTP-заголовков для безопасности
Компании, продающие «системы показателей безопасности», сейчас на подъеме, их влияние в сфере корпоративных продаж растет. К тому же есть те, кого низкий рейтинг безопасности у продавцов смущает, и те, кто хотя бы однажды, глядя на рейтинг, отказался от покупки, — я с такими людьми общался.
Я посмотрел, как эти компании вычисляют показатели безопасности других компаний. Оказалось, они смотрят на сочетание использования НТТР-заголовка для безопасности и репутации IP-адресов.
Репутация IP-адреса основывается на данных черных списков и списков спамеров в сочетании с данными о владельце общедоступного IP-адреса. Она, в принципе, должна быть чистой, если ваша компания не рассылает спам и в состоянии быстро определить и остановить вредоносное внедрение. Использование заголовка безопасности НТТР вычисляется аналогично тому, как работает Observatory от Mozilla.
Таким образом, рейтинг большинства компаний, в основном, определяется заголовками, включенными на общедоступных веб-сайтах для безопасности.
Настроить заголовок правильно — это недолго (серьезной проверки не потребуется), но это улучшит безопасность сайта и поможет не растерять покупателей, для которых безопасность — не пустой звук
В ценности упомянутой методики проверки я сомневаюсь, к том же ценник за услугу выставлен заоблачный. Вряд ли компании-поставщики этой услуги способны определить безопасность ресурса с заявленной точностью. Впрочем, ситуация лишний раз подчеркивает, как важно сесть и уделить время тому, чтобы включить и настроить правильные заголовки.
В этой статье я буду говорить о наиболее часто проверяемых заголовках, для каждого посоветую значения безопасности и приведу пример настройки. В конце дам примеры настройки для распространенных приложений и веб-серверов.
Важные заголовки для безопасности
Content-Security-Policy
CSP применяется, чтобы предотвращать межсайтовый скриптинг — путем определения, какие ресурсы могут быть загружены. Из всего списка этот заголовок отнимет больше остальных времени на создание и правильную поддержку, а еще больше других подвержен рискам. Разрабатывая CSP, тщательно его проверяйте — если вдруг заблокируете используемый вами же источник, то нарушите функциональность собственного сайта.
Для предварительной версии можно использовать замечательный инструмент — расширение для браузера Mozilla, Laboratory CSP. Установите его в браузере, тщательно изучите сайт, для которого хотите создать CSP, а после используйте сгенерированную CSP на своем сайте. В идеале, нужно еще переработать JavaScript, так что можно удалить директиву «unsafe inline».
CSP может показаться сложной и сбить с толку, поэтому, если хотите углубиться в тему, посетите официальный сайт.
Предварительно можно настроить CSP так (на боевом сайте она, скорее всего, потребует множества модификаций). Добавьте в каждый раздел вашего сайта домены.
Strict-Transport-Security
Этот заголовок сообщает браузеру, что на сайт заходить можно только по протоколу HTTPS — всегда включайте его, если на вашем сайта активирован HTTPS. Включите его на всех используемых субдоменах, если таковые имеются.
X-Content-Type-Options
Благодаря этому заголовку браузеры придерживаются типов MIME, установленных приложением, что помогает предотвратить часть атак с межсайтовым скриптингом.
Также он снижает риск неожиданного поведения приложения, когда браузер неверно «угадывает» тип контента на сайте — например, если разработчик обозначает страницу «HTML», а браузер видит JavaScript и пытается отрисовать страницу соответственно. Также благодаря этому заголовку браузер всегда держится установленных сервером MIME-типов.
Cache-Control
Этот будет позаковыристее прочих, потому что для разных типов контента вам наверняка нужны разные политики кэширования.
Никакая деликатная информация — вроде страницы пользователя или страницы оплаты товара — не должна кэшироваться. Одна из причин для этого — чтобы другой пользователь компьютера не нажал кнопку «назад», не прочел историю и не увидел личных данных другого пользователя.
Впрочем, кэшировать можно и нужно те страницы, которые обновляются редко, например статические ресурсы (картинки, файлы CSS и JS). Кэширование можно настроить на постраничной основе, или используя regex в настройках сервера.
Expires
Этот заголовок устанавливает время, на которое текущий запрос сохраняется в кэше. Он игнорируется, если включен заголовок Cache-Control max-age, так что мы включаем его только на случай, если его проверяет простенький сканер — без учета контроля кэширования.
Мы предполагаем, что в целях безопасности браузер не кэширует ничего, так что дата в заголовке всегда будет в прошлом.
X-Frame-Options
Этот заголовок разрешает отображение сайта в iFrame.
Поместив ваш веб-сайт в iFrame, вредоносный ресурс получает возможность произвести кликджекинг атаку — запустив некий JavaScript, который обманом вынудит пользователя кликнуть по iFrame, а после начнет взаимодействовать с ресурсом от его, пользователя, имени (то есть человек кликнет по вредоносной ссылке или кнопке, даже не подозревая об этом!).
Этот заголовок всегда надо настраивать на отказ, исключение — если вы намеренно используете фреймы. Тогда заголовок нужно настраивать на тот же источник. Если вы по умолчанию используете фреймы с другим сайтом, занесите сторонний домен в белый список.
Следует также отметить, что этот заголовок замещается директивой CSP frame-ancestors. Я его рекомендую пока включать, но только для того, чтобы заткнуть инструменты для проверки заголовков, в будущем от него скорее всего избавятся.
Access-Control-Allow-Origin
Этот заголовок сообщает браузеру, внешний код каких посторонних сайтов имеет право делать запросы к определенной странице. Настройки по умолчанию обычно правильные, но если надо — можно и поменять.
Например, сайт А содержит некий JavaScript, который хочет сделать запрос к сайту В. Сайт В должен ответить на этот запрос — если заголовок разрешает сайту А сделать запрос. Если нужно настроить множество источников, подробности на странице на MDN.
Тут можно слегка запутаться, поэтому я составил схему — проиллюстрировать, как работает этот заголовок:
Поток данных с Access-Control-Allow-Origin
Set-Cookie
Убедитесь, что ваши cookies устанавливаются только через протокол HTTPS (с шифрованием), и что к ним нет доступа через JavaScript. Эти файлы можно посылать, только если ваш сайт тоже поддерживает HTTPS, как и должно быть. Всегда нужно выставлять вот такие флаги:
Пример определения Cookie:
X-XSS-Protection
Этот заголовок приказывает браузеру прервать выполнение обнаруженных атак межсайтового скриптинга. Включая его, вы не сильно рискуете, но перед запуском в производственную среду все равно протестируйте.
Пример настроек веб-сервера
Вообще, в настройках сервера лучше всего добавлять заголовки на весь сайт. Исключение — файлы cookie, поскольку они определяются в самом приложении.
Советую, прежде чем добавить на сайт заголовки, сперва свериться с Observatory или вручную заглянуть в заголовки — проверить, какие уже установлены. Некоторые движки сайтов и сервера самостоятельно все установят, поэтому просто реализуйте нужные вам заголовки или измените те, которые в этом нуждаются.
Конфигурация Apache
Конфигурация Nginx
Настройка заголовка уровня приложения
Если у вас нет доступа к веб-серверу или требования к настройке заголовков сложные, то вам наверняка придется настроить их в самом приложении. Обычно для всего сайта это осуществляется с помощью промежуточного ПО, или на основе однократной установки заголовков при каждом запросе.
Для краткости в каждый пример я включил только по одному заголовку. Вы же добавляйте по тому же способу все, которые нужны.
Добавьте global mount path:
Java и Spring
У меня мало опыта работы со Spring, но у Baeldung есть отличное руководство по настройке заголовков в Spring.
Я не знаком с разнообразными средами PHP. Ищите промежуточное ПО для запросов. Для единичного запроса все просто.
Python / Django
Django включает настраиваемое промежуточное ПО для обеспечения безопасности, которое выполнит за вас все эти настройки. Активируйте сначала их.
Ответы некоторых страниц можно трактовать как словарь. В Django есть особый способ работы с кэшированием, и если хотите настроить заголовки кэширования таким образом, с ним надо ознакомиться.
Выводы
Настройка заголовков — процесс относительно простой и быстрый, зато дает прирост уровня безопасности сайта — в плане защиты данных, от межсайтового скриптинга и кликджекинга.
В будущем вы оградите себя от срыва сделок, потому что ваш рейтинг безопасности останется на уровне. Практика его оценки на основе рассмотренных мной параметров набирает обороты, и мне кажется, ее роль в ближайшие годы в сфере продаж только усилится.
Дайте знать, если я упустил какой-то важный заголовок!
Настройка CSP (Content Security Policy)
Браузеры пользователей часто заражены приложениями, которые подсовывают им во время сёрфинга по сайтам рекламу сомнительного характера (порно, казино, разводы на деньги и пр.). Несмотря на то, что это проблема со стороны пользователя, Яндекс может дать за это фильтр, понизив позиции или вовсе выкинув из индекса.
Даже если от поисковой системы мы санкции не получим, всё равно можем остаться в проигрыше, т.к. недополучаем деньги: наша реклама подменяется чужой рекламой. Симптомы такой проблемы выражаются внешними переходами по ссылкам, которых нет на ваших сайтах. Например:
Вообще эта проблема старая. Обсуждается давно на серче. Вот как всё начиналось.
Как с этим бороться? Использовать Content Security Policy! Ведь поставив CSP, мы можем еще и доход повысить, а не только предотвратить возможность схватить фильтр.
Алгоритм действий по настройке CSP:
Изучаем внешние источники
Сперва мы должны создать предварительное правило CSP для нашего сайта. Для этого нам нужно знать, какие именно внешние источники используются у нас на сайте.
Ознакомимся с директивами
Правило CSP состоит из директив:
Четыре ключевых слова, которые могут быть использованы в директивах (списке источников):
Далее, просмотрев код нашего сайта, мы начинаем создавать предварительное правило CSP.
Создаем тестовое правило CSP
Тестовое правило Content-Security-Policy для любого сайта будет выглядеть так:
Теперь подключаем наше правило.
Подключаем правило CSP к сайту
Существует несколько способов подключить правила CSP к сайту. Рассмотрим самые распространённые из них:
Вставляем в него следующий код:
Header set Content-Security-Policy «_____»
В коду между строчками и вставляем:
В файле functions.php после кода
Теперь начинается монотонный, немного нудный процесс поиска ошибок и их исправления. Для этого я использовал опять же Оперу (либо можно использовать плагин для ФФ — Firebug). Открываем консоль ошибок браузера (Ctrl+Shift+C) и наблюдаем такую картинку:
Дальше нам нужно перенести все заблокированные домены в соответствующую секцию нашего правила, как понятно из текста ошибки – это script-src. Вносим правки в правило, обновляем код и смотрим, что у нас получилось.
Часть ошибок пропала, но вылезли новые. Это связано с тем, что некоторые скрипты после того, как мы их разблокируем, могут подгружать дополнительные.
Как видно на скриншоте, ошибки связаны с unsafe-eval (использует наш jQuery) для script-src и секцией frame-src. Обновим наше правило.
Последняя ошибка связана с секцией connect-src, добавим данную секцию в наше правило и внесем домен mc.yandex.ru (можно просто *.yandex.ru).
Финальная версия нашего CSP
Ошибок может быть в разы больше, поэтому нужно ещё «походить» по сайту с открытой консолью и понаблюдать за возможными ошибками. Кроме того, проверить на ошибки лучше в нескольких браузерах, например, ещё в Google Chrome.
Результаты
Результат настройки CSP на одном моем сайте:
И вот результат моего коллеги:
Как видите, переходов на «левые» сайты теперь стало гораздо меньше