Как настроить Content Security Policy (CSP)

  • 39 294
  • Комментарии: 21
Content Security Policy

Три года назад организацией Mozilla Foundation был разработан новый стандарт политики безопасности, который предотвращает XSS-атаки и другие, связанные с ним виды атак запрещая подгружать и выполнять скрипты с запрещённых ресурсов. Называется он Content Security Policy (CSP), что в переводе означает «Политика безопасности контента».

На момент написания статьи стандарт CSP находится в статусе Candidate Recommendation, что означает возможное принятие этого стандарта в будущем W3C консорциумом. На данный момент все популярные браузеры поддерживают этот стандарт.

Поддержка Content Security Policy в различных браузерах
Браузер Версия Примечания
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 — это заголовок, который сервер отправляет браузеру. Давайте разберём более детально из чего же он состоит.

Директивы заголовка CSP
Директива Назначение
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 в консоли Google Chrome

Чтобы лучше понять как работает Content Security Policy, давайте немного поэкспериментируем. Создайте файл index.php и напишите в него следующий код:

<?php
$csp = "Content-Security-Policy-Report-Only: "
       . "default-src 'self'; "
       . "report-uri http://localhost/csp/collector.php";
header($csp);
?>
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>CSP test</title>
</head>
<body>
<?php
    foreach (headers_list() as $header) {
        echo $header . '<br>';
    }
?>
<script>
    console.log('Инлайн скрипт работает!');
</script>
<img src="https://zabolotskikh.com/wp-content/themes/fresh/img/logo.gif" alt="Логотип"/>
</body>
</html>

Обратите внимание, что в http заголовке я указал Content-Security-Policy-Report-Only он аналогичен Content-Security-Policy, с той лишь разницей, что не блокирует ресурсы, а только оповещает о нарушении. Крайне полезная штука при тестировании системы перед внедрением!

Давайте разберёмся, что же мы понаписали. Первым делом мы указали в http заголовке директиву default-src 'self', что означает что подгружать ресурсы можно только со своего хоста. Любые инлайн скрипты и css запрещены. Ок, идём дальше и видим:

<script>
    console.log('Инлайн скрипт работает!');
</script>
<img src="https://zabolotskikh.com/wp-content/themes/fresh/img/logo.gif" alt="Логотип"/>

Т.е. попробуем выполнить инлайн скрипт и загрузить картинку со стороннего хоста. И посмотрим как отреагирует наш бравый защитник:
Content Security Policy Report Only
CSP отреагировал адекватно. Т.е. подгрузил картинку и выполнил инлайновый javascript, но при этом сказал нам в консоли «ата-та!», а именно: сообщил о том, что произошло два нарушения.

Теперь давайте изменим заголовок с Content-Security-Policy-Report-Only на Content-Security-Policy и посмотрим что будет:
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):

 {
  "csp-report": {
    "document-uri": "http://localhost/csp/",
    "referrer": "",
    "violated-directive": "default-src 'self'",
    "original-policy": "default-src 'self'; report-uri http://localhost/csp/collector.php",
    "blocked-uri": "https://zabolotskikh.com"
  }
}

С этим отчётом вы можете делать всё что угодно, например сохранять в базу, отправлять на почту. Я предлагаю записывать все нарушения в csv файл. Давайте сделаем это!

Создайте файл collector.php и напишите в него следующие строки:

<?php

http_response_code(204);

$report = file_get_contents('php://input');
$report = json_decode($report, true);

if (empty($report)) {
	exit;
}

$report = $report['csp-report'];

$delimiter = '|';
$csvLine = '';
$csvLine .= !empty($report['document-uri']) ? $report['document-uri'] : $delimiter;
$csvLine .= !empty($report['referrer']) ? $delimiter . $report['referrer'] : $delimiter;
$csvLine .= !empty($report['violated-directive']) ? $delimiter . $report['violated-directive'] : $delimiter;
$csvLine .= !empty($report['original-policy']) ? $delimiter . $report['original-policy'] : $delimiter;
$csvLine .= !empty($report['blocked-uri']) ? $delimiter . $report['blocked-uri'] : $delimiter;
$csvLine .= !empty($report['status-code']) ? $delimiter . $report['status-code'] : $delimiter;
$csvLine .= "\r\n";

file_put_contents('report.csv', $csvLine, FILE_APPEND);

Теперь ещё раз обновите страницу и посмотрите в директорию http://localhost/csp/. У вас должен появиться файл report.csv с двумя строчками кода:
Content Security Policy Обработанный отчёт
Ура! Мы поймали отчёт о нарушениях и записали его в файл. Можете показать этот файл друзьям 😉 А лучше всего начните внедрять CSP на свой сайт, сначала в режиме тестирования, а потом в «боевом» виде. На этапе тестирования отчёт поможет вам анализировать какие директивы реагируют на нарушения и соответствующим образом настраивать их.

Скачать файлы с примерами из статьи

Полезные материалы по Content Security Policy

Понравилась статья? Оцени её!
21 комментарий
  • Евгений пишет:

    А можно подробней, что куда писать в wordpress?

  • ZULI пишет:

    а что за прикол такой в логах бывает ………./collector.php|data|200
    что бы это могло значить?

  • Анна пишет:

    Подскажите, а есть разница какой doctype?
    Не могу понять почему у меня на одном сайте обрезается политика CSP.

  • Сергей пишет:

    Борис помоги пожалуйста составить Content Security Policy для моего сайта http://www.cherneenet.ru

    • Борис Заболотских пишет:

      Сергей, благодарю за обращение, но у меня на данный момент нет на это времени.

      • Сергей пишет:

        А может подскажешь кого-нибудь, я уже весь инет перерыл, толку ноль. Может какие-нибудь сервисы есть?

        • Борис Заболотских пишет:

          У тебя сайт на WP, для него же специальный плагин есть: http://people.mozilla.org/~bsterne/content-security-policy/wordpress.html
          Я поспрашиваю у друзей, может кто сможет помочь, но это не бесплатно будет.

          • Сергей пишет:

            Пробовал плагин, не работает, он уже 5 лет не обновлялся. Спроси у друзей, потом напишешь мне. Спасибо.

  • АРтем пишет:

    ВСе сдела по инструкции, Report не работате.!

  • Serg пишет:

    Привет! А не подскажешь такой момент.
    Благополучно блокировал яндекс советник через CSP до недавнего времени(( Конкретно в google chrome перестало блокировать… похоже обновили расширение и теперь подгружают через него или еще как.. Вобщем долбанный советник нашел лазейку в Хроме(( Есть какие то варианты блочить расширения хрома.. или типа того?

  • Сергей П. пишет:

    Оценка статьи — 4 балла.

    Не хватает ссылки на спецификацию: http://w3c.github.io/webappsec-csp/

    Не сказано, что может использоваться для указания источника, помимо ключевых слов и URL (см. спецификацию, раздел 2.2.1. Source Lists)

  • Миха пишет:

    Здравствуйте.

    А куда вставлять X-WebKit-CSP и X-Content-Security-Policy в примере:
    $csp = «Content-Security-Policy-Report-Only: »
    . «default-src ‘self’; »
    . «report-uri http://localhost/csp/collector.php«;
    header($csp);

  • Станислав пишет:

    Здравствуйте! А вы можете помочь с внедрением этой технологии на сайт на платной основе?

  • WPuse.ru пишет:

    Существует ли CSP, который полностью совместим с Гугл Адсенс? Он же подгружает множество других хостов….

  • Pavel пишет:

    Пробую дать доступ на своем сайте для выгрузки, и не пашит Сайт

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *