Уменьшите риск межсайтового скриптинга (XSS) с помощью строгой политики безопасности контента (CSP).

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

  • Хром: 52.
  • Край: 79.
  • Фаерфокс: 52.
  • Сафари: 15.4.

Источник

Межсайтовый скриптинг (XSS) , возможность внедрения вредоносных скриптов в веб-приложение, уже более десяти лет является одной из крупнейших уязвимостей веб-безопасности.

Политика безопасности контента (CSP) — это дополнительный уровень безопасности, который помогает снизить риск XSS. Чтобы настроить CSP, добавьте HTTP-заголовок Content-Security-Policy на веб-страницу и установите значения, которые контролируют, какие ресурсы пользовательский агент может загружать для этой страницы.

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

Ключевой термин: одноразовый номер — это случайное число, используемое только один раз, которое вы можете использовать, чтобы пометить тег <script> как доверенный.

Ключевой термин: хеш-функция — это математическая функция, которая преобразует входное значение в сжатое числовое значение, называемое хешем . Вы можете использовать хэш (например, SHA-256 ), чтобы пометить встроенный тег <script> как доверенный.

Политику безопасности контента, основанную на одноразовых значениях или хэшах, часто называют строгим CSP . Когда приложение использует строгий CSP, злоумышленники, обнаружившие недостатки внедрения HTML, обычно не могут использовать их, чтобы заставить браузер выполнить вредоносные сценарии в уязвимом документе. Это связано с тем, что строгий CSP допускает только хешированные сценарии или сценарии с правильным значением nonce, сгенерированным на сервере, поэтому злоумышленники не могут выполнить сценарий, не зная правильного значения nonce для данного ответа.

Почему вам следует использовать строгий CSP?

Если на вашем сайте уже есть CSP, который выглядит как script-src www.googleapis.com , он, вероятно, неэффективен против межсайтового взаимодействия. Этот тип CSP называется CSP белого списка . Они требуют тщательной настройки и могут быть обойдены злоумышленниками.

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

Строгая структура CSP

Базовая строгая политика безопасности контента использует один из следующих заголовков ответа HTTP:

Строгий CSP на основе nonce

Content-Security-Policy:
  script-src 'nonce-{RANDOM}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';
Как работает строгий CSP на основе nonce.

Строгий CSP на основе хэша

Content-Security-Policy:
  script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Следующие свойства делают такого CSP «строгим» и, следовательно, безопасным:

  • Он использует одноразовые номера 'nonce-{RANDOM}' или хэши 'sha256-{HASHED_INLINE_SCRIPT}' чтобы указать, какие теги <script> доверяет разработчику сайта выполняться в браузере пользователя.
  • Он устанавливает 'strict-dynamic' чтобы уменьшить усилия по развертыванию CSP на основе одноразового номера или хэша, автоматически разрешая выполнение сценариев, созданных доверенным сценарием. Это также разблокирует использование большинства сторонних библиотек JavaScript и виджетов.
  • Он не основан на списках разрешенных URL-адресов, поэтому не подвержен обычным обходам CSP .
  • Он блокирует ненадежные встроенные сценарии, такие как встроенные обработчики событий или javascript: URI.
  • Он ограничивает object-src отключением опасных плагинов, таких как Flash.
  • Он ограничивает base-uri , чтобы блокировать внедрение тегов <base> . Это не позволяет злоумышленникам изменить расположение скриптов, загруженных с относительных URL-адресов.

Примите строгий CSP

Чтобы принять строгий CSP, вам необходимо:

  1. Решите, должно ли ваше приложение устанавливать CSP на основе одноразового номера или хэша.
  2. Скопируйте CSP из раздела структуры Strict CSP и установите его в качестве заголовка ответа во всем приложении.
  3. Выполните рефакторинг HTML-шаблонов и клиентского кода для удаления шаблонов, несовместимых с CSP.
  4. Разверните свой CSP.

Вы можете использовать аудит Lighthouse (v7.3.0 и выше с флагом --preset=experimental ) на протяжении всего этого процесса, чтобы проверить, есть ли на вашем сайте CSP и достаточно ли он строг, чтобы быть эффективным против XSS.

Отчет Lighthouse предупреждает, что в режиме принудительного применения не найден ни один CSP.
Если на вашем сайте нет CSP, Lighthouse покажет это предупреждение.

Шаг 1. Решите, нужен ли вам CSP на основе одноразового номера или хэша.

Вот как работают два типа строгого CSP:

CSP на основе Nonce

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

Используйте CSP на основе nonce для HTML-страниц, отображаемых на сервере. Для этих страниц вы можете создать новое случайное число для каждого ответа.

CSP на основе хэша

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

Используйте CSP на основе хэша для HTML-страниц, обслуживаемых статически, или страниц, которые необходимо кэшировать. Например, вы можете использовать CSP на основе хэша для одностраничных веб-приложений, созданных с помощью таких платформ, как Angular, React или других, которые статически обслуживаются без рендеринга на стороне сервера.

Шаг 2. Установите строгий CSP и подготовьте сценарии.

При настройке CSP у вас есть несколько вариантов:

  • Режим «только отчет» ( Content-Security-Policy-Report-Only ) или режим принудительного применения ( Content-Security-Policy ). В режиме только отчетов CSP пока не блокирует ресурсы, поэтому на вашем сайте ничего не ломается, но вы можете видеть ошибки и получать отчеты обо всем, что могло быть заблокировано. Локально, когда вы настраиваете CSP, это не имеет особого значения, поскольку в обоих режимах ошибки отображаются в консоли браузера. Во всяком случае, режим принудительного применения может помочь вам найти ресурсы, которые блокирует ваш проект CSP, поскольку блокировка ресурса может сделать вашу страницу неработающей. Режим «только отчет» становится наиболее полезным на более позднем этапе процесса (см. шаг 5 ).
  • Заголовок или HTML-тег <meta> . Для локальной разработки тег <meta> может быть более удобным для настройки вашего CSP и быстрого просмотра того, как он влияет на ваш сайт. Однако:
    • Позже, при развертывании вашего CSP в рабочей среде, мы рекомендуем установить его как заголовок HTTP.
    • Если вы хотите настроить CSP в режиме «только отчет», вам необходимо установить его в качестве заголовка, поскольку метатеги CSP не поддерживают режим «только отчет».

Вариант А: CSP на основе Nonce

Установите следующий заголовок HTTP-ответа Content-Security-Policy в своем приложении:

Content-Security-Policy:
  script-src 'nonce-{RANDOM}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Создать одноразовый номер для CSP

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

  • Криптографически стойкое случайное значение (в идеале длиной более 128 бит).
  • Создается заново для каждого ответа
  • Кодировка Base64

Вот несколько примеров того, как добавить одноразовый номер CSP в серверные платформы:

const app = express();

app.get('/', function(request, response) {
  // Generate a new random nonce value for every response.
  const nonce = crypto.randomBytes(16).toString("base64");

  // Set the strict nonce-based CSP response header
  const csp = `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`;
  response.set("Content-Security-Policy", csp);

  // Every <script> tag in your application should set the `nonce` attribute to this value.
  response.render(template, { nonce: nonce });
});

Добавьте атрибут nonce к элементам <script>

При использовании CSP на основе nonce каждый элемент <script> должен иметь атрибут nonce , соответствующий случайному значению nonce, указанному в заголовке CSP. Все скрипты могут иметь один и тот же nonce. Первым шагом является добавление этих атрибутов во все сценарии, чтобы CSP разрешал их использование.

Вариант Б. Заголовок ответа CSP на основе хеша

Установите следующий заголовок HTTP-ответа Content-Security-Policy в своем приложении:

Content-Security-Policy:
  script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Для нескольких встроенных скриптов синтаксис следующий: 'sha256-{HASHED_INLINE_SCRIPT_1}' 'sha256-{HASHED_INLINE_SCRIPT_2}' .

Загружать исходные скрипты динамически

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

Пример того, как встроить ваши скрипты.
Разрешено CSP
<script>
  var scripts = [ 'https://example.org/foo.js', 'https://example.org/bar.js'];

  scripts.forEach(function(scriptUrl) {
    var s = document.createElement('script');
    s.src = scriptUrl;
    s.async = false; // to preserve execution order
    document.head.appendChild(s);
  });
</script>
Чтобы разрешить запуск этого сценария, необходимо вычислить хэш встроенного сценария и добавить его в заголовок ответа CSP, заменив заполнитель {HASHED_INLINE_SCRIPT} . Чтобы уменьшить количество хэшей, вы можете объединить все встроенные скрипты в один скрипт. Чтобы увидеть это в действии, обратитесь к этому примеру и его коду .
Заблокировано CSP
<script src="https://example.org/foo.js"></script>
<script src="https://example.org/bar.js"></script>
CSP блокирует эти сценарии, поскольку хешировать можно только встроенные сценарии.

Рекомендации по загрузке скрипта

В примере встроенного скрипта добавлено s.async = false чтобы гарантировать, что foo выполняется до bar , даже если bar загружается первым. В этом фрагменте s.async = false не блокирует синтаксический анализатор во время загрузки сценариев, поскольку сценарии добавляются динамически. Анализатор останавливается только во время выполнения сценариев, как и в случае async сценариев. Однако, используя этот фрагмент, имейте в виду:

  • Один или оба сценария могут выполниться до завершения загрузки документа. Если вы хотите, чтобы документ был готов к моменту выполнения сценариев, дождитесь события DOMContentLoaded прежде чем добавлять сценарии. Если это вызывает проблемы с производительностью из-за того, что сценарии не начинают загружаться достаточно рано, используйте теги предварительной загрузки ранее на странице.
  • defer = true ничего не делает. Если вам нужно такое поведение, запустите сценарий вручную, когда это необходимо.

Шаг 3. Рефакторинг HTML-шаблонов и клиентского кода.

Для запуска сценариев можно использовать встроенные обработчики событий (например onclick="…" , onerror="…" ) и URI JavaScript ( <a href="javascript:…"> ). Это означает, что злоумышленник, обнаруживший ошибку XSS, может внедрить этот тип HTML и выполнить вредоносный JavaScript. CSP на основе одноразового номера или хэша запрещает использование такого типа разметки. Если на вашем сайте используется какой-либо из этих шаблонов, вам необходимо преобразовать их в более безопасные альтернативы.

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

Отчеты о нарушении CSP в консоли разработчика Chrome.
Ошибки консоли для заблокированного кода.

В большинстве случаев исправить это просто:

Рефакторинг встроенных обработчиков событий

Разрешено CSP
<span id="things">A thing.</span>
<script nonce="${nonce}">
  document.getElementById('things').addEventListener('click', doThings);
</script>
CSP позволяет использовать обработчики событий, зарегистрированные с помощью JavaScript.
Заблокировано CSP
<span onclick="doThings();">A thing.</span>
CSP блокирует встроенные обработчики событий.

Рефакторинг javascript: URI

Разрешено CSP
<a id="foo">foo</a>
<script nonce="${nonce}">
  document.getElementById('foo').addEventListener('click', linkClicked);
</script>
CSP позволяет использовать обработчики событий, зарегистрированные с помощью JavaScript.
Заблокировано CSP
<a href="javascript:linkClicked()">foo</a>
CSP блокирует javascript: URI.

Удалите eval() из вашего JavaScript.

Если ваше приложение использует eval() для преобразования сериализации строк JSON в объекты JS, вам следует провести рефакторинг таких экземпляров в JSON.parse() , что также быстрее .

Если вы не можете удалить все случаи использования eval() , вы все равно можете установить строгий CSP на основе nonce, но вам придется использовать ключевое слово CSP 'unsafe-eval' , что делает вашу политику немного менее безопасной.

Вы можете найти эти и другие примеры такого рефакторинга в этой строгой кодовой лаборатории CSP:

Шаг 4 (необязательно). Добавьте резервные варианты для поддержки старых версий браузера.

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

  • Хром: 52.
  • Край: 79.
  • Фаерфокс: 52.
  • Сафари: 15.4.

Источник

Если вам нужна поддержка старых версий браузера:

  • Использование strict-dynamic требует добавления https: в качестве запасного варианта для более ранних версий Safari. Когда вы это сделаете:
    • Все браузеры, поддерживающие strict-dynamic игнорируют резервный вариант https: поэтому это не уменьшит силу политики.
    • В старых браузерах сценарии извне могут загружаться только в том случае, если они исходят из HTTPS-источника. Это менее безопасно, чем строгий CSP, но все же предотвращает некоторые распространенные причины XSS, такие как внедрение javascript: URI.
  • Чтобы обеспечить совместимость с очень старыми версиями браузеров (4+ лет), вы можете добавить unsafe-inline в качестве запасного варианта. Все последние браузеры игнорируют unsafe-inline если присутствует nonce или хеш CSP.
Content-Security-Policy:
  script-src 'nonce-{random}' 'strict-dynamic' https: 'unsafe-inline';
  object-src 'none';
  base-uri 'none';

Шаг 5. Разверните CSP

Убедившись, что ваш CSP не блокирует никакие законные сценарии в вашей локальной среде разработки, вы можете развернуть свой CSP в промежуточной, а затем в производственной среде:

  1. (Необязательно) Разверните CSP в режиме «только отчет», используя заголовок Content-Security-Policy-Report-Only . Режим «Только отчеты» удобен для тестирования потенциально критических изменений, таких как новый CSP в рабочей среде, прежде чем вы начнете применять ограничения CSP. В режиме только отчетов ваш CSP не влияет на поведение вашего приложения, но браузер по-прежнему генерирует консольные отчеты об ошибках и нарушениях, когда обнаруживает шаблоны, несовместимые с вашим CSP, поэтому вы можете увидеть, что могло бы сломаться для ваших конечных пользователей. Дополнительную информацию см. в разделе Reporting API .
  2. Если вы уверены, что ваш CSP не нарушит работу вашего сайта для конечных пользователей, разверните свой CSP, используя заголовок ответа Content-Security-Policy . Мы рекомендуем настроить CSP с использованием HTTP-заголовка на стороне сервера, поскольку он более безопасен, чем тег <meta> . После выполнения этого шага ваш CSP начнет защищать ваше приложение от XSS.

Ограничения

Строгий CSP обычно обеспечивает надежный дополнительный уровень безопасности, который помогает снизить риск XSS. В большинстве случаев CSP значительно уменьшает поверхность атаки, отвергая опасные шаблоны, такие как javascript: URI. Однако в зависимости от типа используемого вами CSP (nonce, хэши, со 'strict-dynamic' или без него) бывают случаи, когда CSP также не защищает ваше приложение:

  • Если вы используете сценарий nonce, но есть внедрение непосредственно в тело или параметр src этого элемента <script> .
  • При наличии внедрений в местоположения динамически создаваемых скриптов ( document.createElement('script') ), в том числе в любые библиотечные функции, создающие узлы DOM script на основе значений их аргументов. Сюда входят некоторые распространенные API, такие как .html() в jQuery, а также .get() и .post() в jQuery < 3.0.
  • Если в старых приложениях AngularJS есть внедрение шаблонов. Злоумышленник, который может внедрить шаблон AngularJS, может использовать его для выполнения произвольного JavaScript .
  • Если политика содержит 'unsafe-eval' , инъекции в eval() , setTimeout() и несколько других редко используемых API.

Разработчики и инженеры по безопасности должны обращать особое внимание на такие шаблоны во время проверок кода и аудитов безопасности. Более подробную информацию об этих случаях можно найти в документе «Политика безопасности контента: успешный беспорядок между усилением защиты и смягчением последствий» .

Дальнейшее чтение

,

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

  • Хром: 52.
  • Край: 79.
  • Фаерфокс: 52.
  • Сафари: 15.4.

Источник

Межсайтовый скриптинг (XSS) , возможность внедрения вредоносных скриптов в веб-приложение, уже более десяти лет является одной из крупнейших уязвимостей веб-безопасности.

Политика безопасности контента (CSP) — это дополнительный уровень безопасности, который помогает снизить риск XSS. Чтобы настроить CSP, добавьте HTTP-заголовок Content-Security-Policy на веб-страницу и установите значения, которые контролируют, какие ресурсы пользовательский агент может загружать для этой страницы.

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

Ключевой термин: одноразовый номер — это случайное число, используемое только один раз, которое можно использовать для пометки тега <script> как доверенного.

Ключевой термин: хеш-функция — это математическая функция, которая преобразует входное значение в сжатое числовое значение, называемое хешем . Вы можете использовать хэш (например, SHA-256 ), чтобы пометить встроенный тег <script> как доверенный.

Политику безопасности контента, основанную на одноразовых значениях или хэшах, часто называют строгим CSP . Когда приложение использует строгий CSP, злоумышленники, обнаружившие недостатки внедрения HTML, обычно не могут использовать их, чтобы заставить браузер выполнить вредоносные сценарии в уязвимом документе. Это связано с тем, что строгий CSP допускает только хешированные сценарии или сценарии с правильным значением nonce, сгенерированным на сервере, поэтому злоумышленники не могут выполнить сценарий, не зная правильного значения nonce для данного ответа.

Почему вам следует использовать строгий CSP?

Если на вашем сайте уже есть CSP, который выглядит как script-src www.googleapis.com , он, вероятно, неэффективен против межсайтового взаимодействия. Этот тип CSP называется CSP белого списка . Они требуют тщательной настройки и могут быть обойдены злоумышленниками.

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

Строгая структура CSP

Базовая строгая политика безопасности контента использует один из следующих заголовков ответа HTTP:

Строгий CSP на основе nonce

Content-Security-Policy:
  script-src 'nonce-{RANDOM}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';
Как работает строгий CSP на основе nonce.

Строгий CSP на основе хэша

Content-Security-Policy:
  script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Следующие свойства делают такого CSP «строгим» и, следовательно, безопасным:

  • Он использует одноразовые номера 'nonce-{RANDOM}' или хэши 'sha256-{HASHED_INLINE_SCRIPT}' чтобы указать, какие теги <script> доверяет разработчику сайта выполняться в браузере пользователя.
  • Он устанавливает 'strict-dynamic' чтобы упростить развертывание CSP на основе одноразового номера или хэша, автоматически разрешая выполнение сценариев, созданных доверенным сценарием. Это также разблокирует использование большинства сторонних библиотек и виджетов JavaScript.
  • Он не основан на списках разрешенных URL-адресов, поэтому не подвержен обычным обходам CSP .
  • Он блокирует ненадежные встроенные сценарии, такие как встроенные обработчики событий или javascript: URI.
  • Он ограничивает object-src отключением опасных плагинов, таких как Flash.
  • Он ограничивает base-uri , чтобы блокировать внедрение тегов <base> . Это не позволяет злоумышленникам изменить расположение скриптов, загруженных с относительных URL-адресов.

Примите строгий CSP

Чтобы принять строгий CSP, вам необходимо:

  1. Решите, должно ли ваше приложение устанавливать CSP на основе одноразового номера или хэша.
  2. Скопируйте CSP из раздела структуры Strict CSP и установите его в качестве заголовка ответа во всем приложении.
  3. Выполните рефакторинг HTML-шаблонов и клиентского кода для удаления шаблонов, несовместимых с CSP.
  4. Разверните свой CSP.

Вы можете использовать аудит Lighthouse (v7.3.0 и выше с флагом --preset=experimental ) на протяжении всего этого процесса, чтобы проверить, есть ли на вашем сайте CSP и достаточно ли он строг, чтобы быть эффективным против XSS.

Отчет Lighthouse предупреждает, что в режиме принудительного применения не найден ни один CSP.
Если на вашем сайте нет CSP, Lighthouse покажет это предупреждение.

Шаг 1. Решите, нужен ли вам CSP на основе одноразового номера или хеш-кода.

Вот как работают два типа строгого CSP:

CSP на основе Nonce

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

Используйте CSP на основе nonce для HTML-страниц, отображаемых на сервере. Для этих страниц вы можете создать новое случайное число для каждого ответа.

CSP на основе хэша

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

Используйте CSP на основе хэша для HTML-страниц, обслуживаемых статически, или страниц, которые необходимо кэшировать. Например, вы можете использовать CSP на основе хэша для одностраничных веб-приложений, созданных с помощью таких платформ, как Angular, React или других, которые статически обслуживаются без рендеринга на стороне сервера.

Шаг 2. Установите строгий CSP и подготовьте сценарии.

При настройке CSP у вас есть несколько вариантов:

  • Режим «только отчет» ( Content-Security-Policy-Report-Only ) или режим принудительного применения ( Content-Security-Policy ). В режиме только отчетов CSP пока не блокирует ресурсы, поэтому на вашем сайте ничего не ломается, но вы можете видеть ошибки и получать отчеты обо всем, что могло быть заблокировано. Локально, когда вы настраиваете CSP, это не имеет особого значения, поскольку в обоих режимах ошибки отображаются в консоли браузера. Во всяком случае, режим принудительного применения может помочь вам найти ресурсы, которые блокирует ваш проект CSP, поскольку блокировка ресурса может сделать вашу страницу неработающей. Режим «только отчет» становится наиболее полезным на более позднем этапе процесса (см. шаг 5 ).
  • Заголовок или HTML-тег <meta> . Для локальной разработки тег <meta> может быть более удобным для настройки вашего CSP и быстрого просмотра того, как он влияет на ваш сайт. Однако:
    • Позже, при развертывании вашего CSP в рабочей среде, мы рекомендуем установить его как заголовок HTTP.
    • Если вы хотите настроить CSP в режиме только отчетов, вам необходимо установить его в качестве заголовка, поскольку метатеги CSP не поддерживают режим только отчетов.

Вариант А: CSP на основе Nonce

Установите следующий заголовок HTTP-ответа Content-Security-Policy в своем приложении:

Content-Security-Policy:
  script-src 'nonce-{RANDOM}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Создать одноразовый номер для CSP

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

  • Криптографически стойкое случайное значение (в идеале длиной более 128 бит).
  • Создается заново для каждого ответа
  • Кодировка Base64

Вот несколько примеров того, как добавить одноразовый номер CSP в серверные платформы:

const app = express();

app.get('/', function(request, response) {
  // Generate a new random nonce value for every response.
  const nonce = crypto.randomBytes(16).toString("base64");

  // Set the strict nonce-based CSP response header
  const csp = `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`;
  response.set("Content-Security-Policy", csp);

  // Every <script> tag in your application should set the `nonce` attribute to this value.
  response.render(template, { nonce: nonce });
});

Добавьте атрибут nonce к элементам <script>

При использовании CSP на основе nonce каждый элемент <script> должен иметь атрибут nonce , соответствующий случайному значению nonce, указанному в заголовке CSP. Все скрипты могут иметь один и тот же nonce. Первым шагом является добавление этих атрибутов во все сценарии, чтобы CSP разрешал их использование.

Вариант Б. Заголовок ответа CSP на основе хеша

Установите следующий заголовок HTTP-ответа Content-Security-Policy в своем приложении:

Content-Security-Policy:
  script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Для нескольких встроенных скриптов синтаксис следующий: 'sha256-{HASHED_INLINE_SCRIPT_1}' 'sha256-{HASHED_INLINE_SCRIPT_2}' .

Загружать исходные скрипты динамически

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

Пример того, как встроить ваши скрипты.
Разрешено CSP
<script>
  var scripts = [ 'https://example.org/foo.js', 'https://example.org/bar.js'];

  scripts.forEach(function(scriptUrl) {
    var s = document.createElement('script');
    s.src = scriptUrl;
    s.async = false; // to preserve execution order
    document.head.appendChild(s);
  });
</script>
Чтобы разрешить запуск этого сценария, необходимо вычислить хэш встроенного сценария и добавить его в заголовок ответа CSP, заменив заполнитель {HASHED_INLINE_SCRIPT} . Чтобы уменьшить количество хэшей, вы можете объединить все встроенные скрипты в один скрипт. Чтобы увидеть это в действии, обратитесь к этому примеру и его коду .
Заблокировано CSP
<script src="https://example.org/foo.js"></script>
<script src="https://example.org/bar.js"></script>
CSP блокирует эти сценарии, поскольку хешировать можно только встроенные сценарии.

Рекомендации по загрузке скрипта

В примере встроенного скрипта добавлено s.async = false чтобы гарантировать, что foo выполняется до bar , даже если bar загружается первым. В этом фрагменте s.async = false не блокирует синтаксический анализатор во время загрузки сценариев, поскольку сценарии добавляются динамически. Анализатор останавливается только во время выполнения сценариев, как и в случае async сценариев. Однако, используя этот фрагмент, имейте в виду:

  • Один или оба сценария могут выполниться до завершения загрузки документа. Если вы хотите, чтобы документ был готов к моменту выполнения сценариев, дождитесь события DOMContentLoaded прежде чем добавлять сценарии. Если это вызывает проблемы с производительностью из-за того, что сценарии не начинают загружаться достаточно рано, используйте теги предварительной загрузки ранее на странице.
  • defer = true ничего не делает. Если вам нужно такое поведение, запустите сценарий вручную, когда это необходимо.

Шаг 3. Рефакторинг HTML-шаблонов и клиентского кода.

Для запуска сценариев можно использовать встроенные обработчики событий (например onclick="…" , onerror="…" ) и URI JavaScript ( <a href="javascript:…"> ). Это означает, что злоумышленник, обнаруживший ошибку XSS, может внедрить этот тип HTML и выполнить вредоносный JavaScript. CSP на основе одноразового номера или хэша запрещает использование такого типа разметки. Если на вашем сайте используется какой-либо из этих шаблонов, вам необходимо преобразовать их в более безопасные альтернативы.

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

Отчеты о нарушении CSP в консоли разработчика Chrome.
Ошибки консоли для заблокированного кода.

В большинстве случаев исправить это просто:

Рефакторинг встроенных обработчиков событий

Разрешено CSP
<span id="things">A thing.</span>
<script nonce="${nonce}">
  document.getElementById('things').addEventListener('click', doThings);
</script>
CSP позволяет использовать обработчики событий, зарегистрированные с помощью JavaScript.
Заблокировано CSP
<span onclick="doThings();">A thing.</span>
CSP блокирует встроенные обработчики событий.

Рефакторинг javascript: URI

Разрешено CSP
<a id="foo">foo</a>
<script nonce="${nonce}">
  document.getElementById('foo').addEventListener('click', linkClicked);
</script>
CSP позволяет использовать обработчики событий, зарегистрированные с помощью JavaScript.
Заблокировано CSP
<a href="javascript:linkClicked()">foo</a>
CSP блокирует javascript: URI.

Удалите eval() из вашего JavaScript.

Если ваше приложение использует eval() для преобразования сериализации строк JSON в объекты JS, вам следует провести рефакторинг таких экземпляров в JSON.parse() , что также быстрее .

Если вы не можете удалить все случаи использования eval() , вы все равно можете установить строгий CSP на основе nonce, но вам придется использовать ключевое слово CSP 'unsafe-eval' , что делает вашу политику немного менее безопасной.

Вы можете найти эти и другие примеры такого рефакторинга в этой строгой кодовой лаборатории CSP:

Шаг 4 (необязательно). Добавьте резервные варианты для поддержки старых версий браузера.

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

  • Хром: 52.
  • Край: 79.
  • Фаерфокс: 52.
  • Сафари: 15.4.

Источник

Если вам нужна поддержка старых версий браузера:

  • Использование strict-dynamic требует добавления https: в качестве запасного варианта для более ранних версий Safari. Когда вы это сделаете:
    • Все браузеры, поддерживающие strict-dynamic игнорируют резервный вариант https: поэтому это не уменьшит силу политики.
    • В старых браузерах внешние сценарии могут загружаться только в том случае, если они исходят из HTTPS-источника. Это менее безопасно, чем строгий CSP, но все же предотвращает некоторые распространенные причины XSS, такие как внедрение javascript: URI.
  • Чтобы обеспечить совместимость с очень старыми версиями браузеров (4+ лет), вы можете добавить unsafe-inline в качестве запасного варианта. Все последние браузеры игнорируют unsafe-inline если присутствует nonce или хэш CSP.
Content-Security-Policy:
  script-src 'nonce-{random}' 'strict-dynamic' https: 'unsafe-inline';
  object-src 'none';
  base-uri 'none';

Шаг 5. Разверните CSP

Убедившись, что ваш CSP не блокирует никакие законные сценарии в вашей локальной среде разработки, вы можете развернуть свой CSP в промежуточной, а затем в производственной среде:

  1. (Необязательно) Разверните CSP в режиме «только отчет», используя заголовок Content-Security-Policy-Report-Only . Режим «только отчет» удобен для тестирования потенциально критических изменений, таких как новый CSP в рабочей среде, прежде чем вы начнете применять ограничения CSP. В режиме только отчетов ваш CSP не влияет на поведение вашего приложения, но браузер по-прежнему генерирует консольные отчеты об ошибках и нарушениях, когда обнаруживает шаблоны, несовместимые с вашим CSP, поэтому вы можете увидеть, что могло бы сломаться для ваших конечных пользователей. Дополнительную информацию см. в разделе Reporting API .
  2. Если вы уверены, что ваш CSP не нарушит работу вашего сайта для конечных пользователей, разверните свой CSP, используя заголовок ответа Content-Security-Policy . Мы рекомендуем настроить CSP с использованием HTTP-заголовка на стороне сервера, поскольку он более безопасен, чем тег <meta> . После выполнения этого шага ваш CSP начнет защищать ваше приложение от XSS.

Ограничения

Строгий CSP обычно обеспечивает надежный дополнительный уровень безопасности, который помогает снизить риск XSS. В большинстве случаев CSP значительно уменьшает поверхность атаки, отвергая опасные шаблоны, такие как javascript: URI. Однако в зависимости от типа CSP, который вы используете (nonce, хэши, со 'strict-dynamic' или без него), бывают случаи, когда CSP также не защищает ваше приложение:

  • Если вы используете сценарий nonce, но есть внедрение непосредственно в тело или параметр src этого элемента <script> .
  • При наличии внедрений в местоположения динамически создаваемых скриптов ( document.createElement('script') ), в том числе в любые библиотечные функции, создающие узлы DOM script на основе значений их аргументов. Сюда входят некоторые распространенные API, такие как .html() в jQuery, а также .get() и .post() в jQuery < 3.0.
  • Если в старых приложениях AngularJS есть внедрение шаблонов. Злоумышленник, который может внедрить шаблон AngularJS, может использовать его для выполнения произвольного JavaScript .
  • Если политика содержит 'unsafe-eval' , инъекции в eval() , setTimeout() и несколько других редко используемых API.

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

Дальнейшее чтение

,

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

  • Хром: 52.
  • Край: 79.
  • Firefox: 52.
  • Сафари: 15.4.

Источник

Сценарии поперечного сайта (XSS) , способность вводить вредоносные сценарии в веб-приложение, была одной из самых больших уязвимостей веб-безопасности на протяжении более десяти лет.

Политика безопасности контента (CSP) - это дополнительный уровень безопасности, который помогает смягчить XSS. Чтобы настроить CSP, добавьте HTTP Content-Security-Policy на веб-страницу и установите значения, которые управляют тем, какие ресурсы пользовательский агент может загрузить для этой страницы.

На этой странице объясняется, как использовать CSP на основе NONCES или HASH для смягчения XSS, вместо обще используемых CSP на основе хост-аллевиста, которые часто оставляют страницу подвергать воздействию XSS, потому что они могут быть оборудованы в большинстве конфигураций .

Ключевой термин: Nonce - это случайное число, используемое только после того, как вы можете использовать для отметки тега <script> как доверительное.

Ключевой термин: хэш -функция - это математическая функция, которая преобразует входное значение в сжатое численное значение, называемое HASH . Вы можете использовать хэш (например, SHA-256 ), чтобы отметить встроенный тег <script> как доверительный.

Политика безопасности контента, основанная на нематке или хэше, часто называют строгим CSP . Когда приложение использует строгий CSP, злоумышленники, которые находят недостатки HTML -инъекции, как правило, не могут использовать их, чтобы заставить браузер выполнять вредоносные сценарии в уязвимом документе. Это связано с тем, что строгая CSP позволяет только хэш -сценариям или сценариям с правильным значением нера, сгенерированным на сервере, поэтому злоумышленники не могут выполнить скрипт, не зная правильного нерею для данного ответа.

Почему вы должны использовать строгий CSP?

Если на вашем сайте уже есть CSP, который выглядит как script-src www.googleapis.com , он, вероятно, не эффективен против кросс-сайта. Этот тип CSP называется AllowList CSP . Они требуют большой настройки и могут быть обойдены злоумышленниками.

Строгие CSP, основанные на криптографических номерах или хэше, избегают этих ловушек.

Строгая структура CSP

Основная строгая политика безопасности контента использует один из следующих заголовков ответов HTTP:

Основанный на основе CSP

Content-Security-Policy:
  script-src 'nonce-{RANDOM}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';
Как работает строгая CSP, основанная на нере.

Хеш-строгий CSP

Content-Security-Policy:
  script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Следующие свойства делают CSP, подобный этой, «строгим» и, следовательно, безопасными:

  • Он использует Nonces 'nonce-{RANDOM}' или хэши 'sha256-{HASHED_INLINE_SCRIPT}' чтобы указать, какой <script> помечает разработчик сайта для выполнения в браузере пользователя.
  • Он устанавливает 'strict-dynamic' чтобы уменьшить усилия по развертыванию CSP, не являющейся HASH, автоматически позволяя выполнять сценарии, которые создает доверенный сценарий. Это также разблокирует использование большинства сторонних библиотек и виджетов JavaScript.
  • Это не основано на разрешении на URL -адреса, поэтому он не страдает от общих обхода CSP .
  • Он блокирует ненадежные встроенные сценарии, такие как встроенные обработчики событий или javascript: Uris.
  • Он ограничивает object-src отключением опасных плагинов, таких как Flash.
  • Он ограничивает base-uri блокировать инъекцию <base> тегов. Это мешает злоумышленникам изменить местоположение сценариев, загруженных из относительных URL -адресов.

Принять строгий CSP

Чтобы принять строгий CSP, вам нужно:

  1. Решите, должно ли ваше приложение установить CSP, не являющуюся HASH, или на основе хеш.
  2. Скопируйте CSP из строгой раздела структуры CSP и установите его в качестве заголовка ответа через ваше приложение.
  3. Рефактор HTML Шаблоны и код на стороне клиента для удаления шаблонов, которые несовместимы с CSP.
  4. Развернуть свой CSP.

Вы можете использовать маяк (v7.3.0 и выше с флагом --preset=experimental ) аудит лучших практик на протяжении всего этого процесса, чтобы проверить, имеет ли ваш сайт CSP, и достаточно строго, чтобы быть эффективным против XSS.

Маяк сообщает о том, что в режиме принуждения не найдено CSP.
Если на вашем сайте нет CSP, Lighthouse показывает это предупреждение.

Шаг 1: Решите, нужен ли вам CSP, не являющийся хеш-на основе хеш-

Вот как работают два типа строгих CSP:

CSP на основе Nonce

С помощью CSP, не основанной на основе, вы генерируете случайное число во время выполнения , включите его в свой CSP и ассоциируете его с каждым тегом сценария на вашей странице. Злоумышленник не может включить или запустить вредоносный сценарий на вашей странице, потому что им нужно угадать правильное случайное число для этого сценария. Это работает только в том случае, если число не догадается, и вновь сгенерировано во время выполнения для каждого ответа.

Используйте CSP, не основанный на основе, для HTML-страниц, отображаемых на сервере. Для этих страниц вы можете создать новое случайное число для каждого ответа.

Хэш-CSP

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

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

Шаг 2: Установите строгий CSP и приготовьте свои сценарии

При настройке CSP у вас есть несколько вариантов:

  • Режим только для сообщений ( Content-Security-Policy-Report-Only ) или режим принуждения ( Content-Security-Policy ). В режиме только в счете CSP еще не блокирует ресурсы, поэтому ничего на вашем сайте разрывается, но вы можете увидеть ошибки и получить отчеты за все, что было бы заблокировано. Локально, когда вы устанавливаете свой CSP, это не имеет значения, потому что оба режима показывают вам ошибки в консоли браузера. Во всяком случае, режим принуждения может помочь вам найти ресурсы ваших черновых блоков CSP, потому что блокировка ресурса может сделать вашу страницу сломанной. Режим только в счете становится наиболее полезным позже в процессе (см. Шаг 5 ).
  • Заголовок или HTML <meta> Tag. Для локальной разработки тег <meta> может быть более удобной для настройки вашего CSP и быстро, когда он влияет на ваш сайт. Однако:
    • Позже, при развертывании вашего CSP в производстве мы рекомендуем установить его в качестве заголовка HTTP.
    • Если вы хотите установить свой CSP в режиме только для отчетов, вам нужно установить его в качестве заголовка, потому что метатеги CSP не поддерживают режим только для отчета.

Вариант A: CSP на основе Nonce

Установите следующий заголовок ответа HTTP Content-Security-Policy в своем приложении:

Content-Security-Policy:
  script-src 'nonce-{RANDOM}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Генерировать Nonce для CSP

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

  • Криптографически сильное случайное значение (в идеале 128+ бит в длину)
  • Недавно сгенерирован для каждого ответа
  • BASE64 кодировано

Вот несколько примеров того, как добавить CSP Nonce в фреймворках на стороне сервера:

const app = express();

app.get('/', function(request, response) {
  // Generate a new random nonce value for every response.
  const nonce = crypto.randomBytes(16).toString("base64");

  // Set the strict nonce-based CSP response header
  const csp = `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`;
  response.set("Content-Security-Policy", csp);

  // Every <script> tag in your application should set the `nonce` attribute to this value.
  response.render(template, { nonce: nonce });
});

Добавить атрибут nonce в элементы <script>

Благодаря CSP на основе нереей каждый элемент <script> должен иметь атрибут nonce , который соответствует случайному не SCE-значению, указанному в заголовке CSP. Все сценарии могут иметь одинаковую нонку. Первый шаг - добавить эти атрибуты ко всем сценариям, чтобы CSP позволял им.

Вариант B: заголовок ответа CSP на основе хэша

Установите следующий заголовок ответа HTTP Content-Security-Policy в своем приложении:

Content-Security-Policy:
  script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Для нескольких встроенных сценариев синтаксис выглядит следующим образом: 'sha256-{HASHED_INLINE_SCRIPT_1}' 'sha256-{HASHED_INLINE_SCRIPT_2}' .

Сценарии нагрузки динамически

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

Пример того, как встроить свои сценарии.
Разрешен CSP
<script>
  var scripts = [ 'https://example.org/foo.js', 'https://example.org/bar.js'];

  scripts.forEach(function(scriptUrl) {
    var s = document.createElement('script');
    s.src = scriptUrl;
    s.async = false; // to preserve execution order
    document.head.appendChild(s);
  });
</script>
Чтобы пропустить этот скрипт, вы должны вычислять хэш встроенного сценария и добавить его в заголовок ответа CSP, заменив заполнитель {HASHED_INLINE_SCRIPT} . Чтобы уменьшить количество хэшей, вы можете объединить все встроенные сценарии в один сценарий. Чтобы увидеть это в действии, обратитесь к этому примеру и его коду .
Заблокирован CSP
<script src="https://example.org/foo.js"></script>
<script src="https://example.org/bar.js"></script>
CSP блокирует эти сценарии, потому что только встроенные подписания могут быть хэшированы.

Скрипт сценария

Пример встроенного сценария добавляет s.async = false чтобы гарантировать, что foo выполняет перед bar , даже если bar загружается первым. В этом фрагменте s.async = false не блокирует анализатор во время загрузки сценариев, потому что сценарии добавляются динамически. Сигнал останавливается только во время выполнения сценариев, как и для async сценариев. Однако, с этим фрагментом, имейте в виду:

  • Один или оба сценария могут выполнить до того, как документ закончил загрузку. Если вы хотите, чтобы документ был готов к моменту выполнения сценариев, дождитесь загруженного события DOMContentLoaded прежде чем добавить сценарии. Если это вызывает проблему с производительностью, потому что сценарии не начинают загружать достаточно рано, используйте теги предварительной загрузки ранее на странице.
  • defer = true ничего не делает. Если вам нужно такое поведение, запустите сценарий вручную, когда это необходимо.

Шаг 3: Рефактор HTML-шаблоны и код на стороне клиента

Встроенные обработчики событий (например, onclick="…" , onerror="…" ) и JavaScript Uris ( <a href="javascript:…"> ) могут использоваться для запуска сценариев. Это означает, что злоумышленник, который считает ошибку XSS, может внедрить этот вид HTML и выполнить вредоносный JavaScript. CSP, не являющийся хеш-на основе, запрещает использование такого рода разметки. Если ваш сайт использует какую -либо из этих моделей, вам нужно рефактировать их в более безопасные альтернативы.

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

Отчеты о нарушении CSP в консоли разработчика Chrome.
Комплексные ошибки для заблокированного кода.

В большинстве случаев исправление простое:

Рефакторные обработчики мероприятий

Разрешен CSP
<span id="things">A thing.</span>
<script nonce="${nonce}">
  document.getElementById('things').addEventListener('click', doThings);
</script>
CSP позволяет обработчикам событий, которые зарегистрированы с использованием JavaScript.
Заблокирован CSP
<span onclick="doThings();">A thing.</span>
CSP блокируют встроенные обработчики событий.

Refactor javascript: Uris

Разрешен CSP
<a id="foo">foo</a>
<script nonce="${nonce}">
  document.getElementById('foo').addEventListener('click', linkClicked);
</script>
CSP позволяет обработчикам событий, которые зарегистрированы с использованием JavaScript.
Заблокирован CSP
<a href="javascript:linkClicked()">foo</a>
CSP блокирует JavaScript: Uris.

Удалить eval() из JavaScript

Если ваше приложение использует eval() для преобразования сериализаций JSON String в объекты JS, вы должны рефакторировать такие экземпляры в JSON.parse() , что также быстрее .

Если вы не можете удалить все использование eval() , вы все равно можете установить строгий CSP, не основанный на нере, но вы должны использовать 'unsafe-eval' , которое делает вашу политику немного менее безопасной.

Вы можете найти это и больше примеров такого рефакторинга в этом строгом CSP CodeLab:

Шаг 4 (необязательно): Добавьте запасные отступления, чтобы поддержать старые версии браузера

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

  • Хром: 52.
  • Край: 79.
  • Firefox: 52.
  • Сафари: 15.4.

Источник

Если вам нужно поддержать старые версии браузера:

  • Использование strict-dynamic требует добавления https: в качестве запасного для более ранних версий Safari. Когда вы делаете это:
    • Все браузеры, которые поддерживают strict-dynamic игнорируют https: MANKBACK, так что это не уменьшит силу политики.
    • В старых браузерах сценарии с внешним источником могут загружаться, только если они поступают из HTTPS Origin. Это менее безопасно, чем строгий CSP, но все же предотвращает некоторые общие XSS, такие как инъекции javascript: URIS.
  • Чтобы обеспечить совместимость с очень старыми версиями браузера (4+ года), вы можете добавить unsafe-inline в качестве запасного. Все недавние браузеры игнорируют unsafe-inline если присутствует NOCE CSP или хэш.
Content-Security-Policy:
  script-src 'nonce-{random}' 'strict-dynamic' https: 'unsafe-inline';
  object-src 'none';
  base-uri 'none';

Шаг 5: развернуть свой CSP

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

  1. (Необязательно) Разверните свой CSP в режиме только для сообщений, используя Content-Security-Policy-Report-Only . Режим только для отчета удобен для проверки потенциально нарушающего изменения, таких как новая CSP в производстве, прежде чем вы начнете обеспечивать соблюдение ограничений CSP. В режиме только в счете ваш CSP не влияет на поведение вашего приложения, но браузер по-прежнему генерирует консольные ошибки и отчеты о нарушении, когда он сталкивается с шаблонами, несовместимыми с вашим CSP, поэтому вы можете увидеть, что бы сломало бы для ваших конечных пользователей. Для получения дополнительной информации см. Отчеты API .
  2. Когда вы уверены, что ваш CSP не сломает ваш сайт для ваших конечных пользователей, разверните свой CSP, используя заголовок ответа Content-Security-Policy . Мы рекомендуем установить ваш CSP с помощью сервера HTTP Hearer Server, потому что он более безопасен, чем тег <meta> . После завершения этого шага ваш CSP начинает защищать ваше приложение от XSS.

Ограничения

Строгий CSP, как правило, обеспечивает сильный дополнительный уровень безопасности, который помогает смягчить XSS. В большинстве случаев CSP значительно снижает поверхность атаки, отвергая опасные паттерны, такие как javascript: URIS. Однако, основываясь на типе CSP, который вы используете (NONCES, HASH, с или без 'strict-dynamic' ), есть случаи, когда CSP также не защищает ваше приложение:

  • Если вы не выбираете сценарий, но есть инъекция непосредственно в тело или параметр src этого элемента <script> .
  • Если есть инъекции в местоположения динамически созданных сценариев ( document.createElement('script') ), в том числе в любые библиотечные функции, которые создают узлы DOM script на основе значений их аргументов. Это включает в себя некоторые общие API, такие как jQuery's .html() , а также .get() и .post() в jQuery <3.0.
  • Если есть инъекции шаблонов в старых приложениях AngularJS. Злоумышленник, который может вводить в шаблон AngularJS, может использовать его для выполнения произвольного JavaScript .
  • Если политика содержит 'unsafe-eval' , инъекции в eval() , setTimeout() и несколько других редко используемых API.

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

Дальнейшее чтение

,

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

  • Хром: 52.
  • Край: 79.
  • Firefox: 52.
  • Сафари: 15.4.

Источник

Сценарии поперечного сайта (XSS) , способность вводить вредоносные сценарии в веб-приложение, была одной из самых больших уязвимостей веб-безопасности на протяжении более десяти лет.

Политика безопасности контента (CSP) - это дополнительный уровень безопасности, который помогает смягчить XSS. Чтобы настроить CSP, добавьте HTTP Content-Security-Policy на веб-страницу и установите значения, которые управляют тем, какие ресурсы пользовательский агент может загрузить для этой страницы.

На этой странице объясняется, как использовать CSP на основе NONCES или HASH для смягчения XSS, вместо обще используемых CSP на основе хост-аллевиста, которые часто оставляют страницу подвергать воздействию XSS, потому что они могут быть оборудованы в большинстве конфигураций .

Ключевой термин: Nonce - это случайное число, используемое только после того, как вы можете использовать для отметки тега <script> как доверительное.

Ключевой термин: хэш -функция - это математическая функция, которая преобразует входное значение в сжатое численное значение, называемое HASH . Вы можете использовать хэш (например, SHA-256 ), чтобы отметить встроенный тег <script> как доверительный.

Политика безопасности контента, основанная на нематке или хэше, часто называют строгим CSP . Когда приложение использует строгий CSP, злоумышленники, которые находят недостатки HTML -инъекции, как правило, не могут использовать их, чтобы заставить браузер выполнять вредоносные сценарии в уязвимом документе. Это связано с тем, что строгая CSP позволяет только хэш -сценариям или сценариям с правильным значением нера, сгенерированным на сервере, поэтому злоумышленники не могут выполнить скрипт, не зная правильного нерею для данного ответа.

Почему вы должны использовать строгий CSP?

Если на вашем сайте уже есть CSP, который выглядит как script-src www.googleapis.com , он, вероятно, не эффективен против кросс-сайта. Этот тип CSP называется AllowList CSP . Они требуют большой настройки и могут быть обойдены злоумышленниками.

Строгие CSP, основанные на криптографических номерах или хэше, избегают этих ловушек.

Строгая структура CSP

Основная строгая политика безопасности контента использует один из следующих заголовков ответов HTTP:

Основанный на основе CSP

Content-Security-Policy:
  script-src 'nonce-{RANDOM}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';
Как работает строгая CSP, основанная на нере.

Хеш-строгий CSP

Content-Security-Policy:
  script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Следующие свойства делают CSP, подобный этой, «строгим» и, следовательно, безопасными:

  • Он использует Nonces 'nonce-{RANDOM}' или хэши 'sha256-{HASHED_INLINE_SCRIPT}' чтобы указать, какой <script> помечает разработчик сайта для выполнения в браузере пользователя.
  • Он устанавливает 'strict-dynamic' чтобы уменьшить усилия по развертыванию CSP, не являющейся HASH, автоматически позволяя выполнять сценарии, которые создает доверенный сценарий. Это также разблокирует использование большинства сторонних библиотек и виджетов JavaScript.
  • Это не основано на разрешении на URL -адреса, поэтому он не страдает от общих обхода CSP .
  • Он блокирует ненадежные встроенные сценарии, такие как встроенные обработчики событий или javascript: Uris.
  • Он ограничивает object-src отключением опасных плагинов, таких как Flash.
  • Он ограничивает base-uri блокировать инъекцию <base> тегов. Это мешает злоумышленникам изменить местоположение сценариев, загруженных из относительных URL -адресов.

Принять строгий CSP

Чтобы принять строгий CSP, вам нужно:

  1. Решите, должно ли ваше приложение установить CSP, не являющуюся HASH, или на основе хеш.
  2. Скопируйте CSP из строгой раздела структуры CSP и установите его в качестве заголовка ответа через ваше приложение.
  3. Рефактор HTML Шаблоны и код на стороне клиента для удаления шаблонов, которые несовместимы с CSP.
  4. Развернуть свой CSP.

Вы можете использовать маяк (v7.3.0 и выше с флагом --preset=experimental ) аудит лучших практик на протяжении всего этого процесса, чтобы проверить, имеет ли ваш сайт CSP, и достаточно строго, чтобы быть эффективным против XSS.

Маяк сообщает о том, что в режиме принуждения не найдено CSP.
Если на вашем сайте нет CSP, Lighthouse показывает это предупреждение.

Шаг 1: Решите, нужен ли вам CSP, не являющийся хеш-на основе хеш-

Вот как работают два типа строгих CSP:

CSP на основе Nonce

С помощью CSP, не основанной на основе, вы генерируете случайное число во время выполнения , включите его в свой CSP и ассоциируете его с каждым тегом сценария на вашей странице. Злоумышленник не может включить или запустить вредоносный сценарий на вашей странице, потому что им нужно угадать правильное случайное число для этого сценария. Это работает только в том случае, если число не догадается, и вновь сгенерировано во время выполнения для каждого ответа.

Используйте CSP, не основанный на основе, для HTML-страниц, отображаемых на сервере. Для этих страниц вы можете создать новое случайное число для каждого ответа.

Хэш-CSP

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

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

Шаг 2: Установите строгий CSP и приготовьте свои сценарии

При настройке CSP у вас есть несколько вариантов:

  • Режим только для сообщений ( Content-Security-Policy-Report-Only ) или режим принуждения ( Content-Security-Policy ). В режиме только в счете CSP еще не блокирует ресурсы, поэтому ничего на вашем сайте разрывается, но вы можете увидеть ошибки и получить отчеты за все, что было бы заблокировано. Локально, когда вы устанавливаете свой CSP, это не имеет значения, потому что оба режима показывают вам ошибки в консоли браузера. Во всяком случае, режим принуждения может помочь вам найти ресурсы ваших черновых блоков CSP, потому что блокировка ресурса может сделать вашу страницу сломанной. Режим только в счете становится наиболее полезным позже в процессе (см. Шаг 5 ).
  • Заголовок или HTML <meta> Tag. Для локальной разработки тег <meta> может быть более удобной для настройки вашего CSP и быстро, когда он влияет на ваш сайт. Однако:
    • Позже, при развертывании вашего CSP в производстве мы рекомендуем установить его в качестве заголовка HTTP.
    • Если вы хотите установить свой CSP в режиме только для отчетов, вам нужно установить его в качестве заголовка, потому что метатеги CSP не поддерживают режим только для отчета.

Вариант A: CSP на основе Nonce

Установите следующий заголовок ответа HTTP Content-Security-Policy в своем приложении:

Content-Security-Policy:
  script-src 'nonce-{RANDOM}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Генерировать Nonce для CSP

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

  • Криптографически сильное случайное значение (в идеале 128+ бит в длину)
  • Недавно сгенерирован для каждого ответа
  • BASE64 кодировано

Вот несколько примеров того, как добавить CSP Nonce в фреймворках на стороне сервера:

const app = express();

app.get('/', function(request, response) {
  // Generate a new random nonce value for every response.
  const nonce = crypto.randomBytes(16).toString("base64");

  // Set the strict nonce-based CSP response header
  const csp = `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`;
  response.set("Content-Security-Policy", csp);

  // Every <script> tag in your application should set the `nonce` attribute to this value.
  response.render(template, { nonce: nonce });
});

Добавить атрибут nonce в элементы <script>

Благодаря CSP на основе нереей каждый элемент <script> должен иметь атрибут nonce , который соответствует случайному не SCE-значению, указанному в заголовке CSP. Все сценарии могут иметь одинаковую нонку. Первый шаг - добавить эти атрибуты ко всем сценариям, чтобы CSP позволял им.

Вариант B: заголовок ответа CSP на основе хэша

Установите следующий заголовок ответа HTTP Content-Security-Policy в своем приложении:

Content-Security-Policy:
  script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Для нескольких встроенных сценариев синтаксис выглядит следующим образом: 'sha256-{HASHED_INLINE_SCRIPT_1}' 'sha256-{HASHED_INLINE_SCRIPT_2}' .

Сценарии нагрузки динамически

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

Пример того, как встроить свои сценарии.
Разрешен CSP
<script>
  var scripts = [ 'https://example.org/foo.js', 'https://example.org/bar.js'];

  scripts.forEach(function(scriptUrl) {
    var s = document.createElement('script');
    s.src = scriptUrl;
    s.async = false; // to preserve execution order
    document.head.appendChild(s);
  });
</script>
Чтобы пропустить этот скрипт, вы должны вычислять хэш встроенного сценария и добавить его в заголовок ответа CSP, заменив заполнитель {HASHED_INLINE_SCRIPT} . Чтобы уменьшить количество хэшей, вы можете объединить все встроенные сценарии в один сценарий. Чтобы увидеть это в действии, обратитесь к этому примеру и его коду .
Заблокирован CSP
<script src="https://example.org/foo.js"></script>
<script src="https://example.org/bar.js"></script>
CSP блокирует эти сценарии, потому что только встроенные подписания могут быть хэшированы.

Скрипт сценария

Пример встроенного сценария добавляет s.async = false чтобы гарантировать, что foo выполняет перед bar , даже если bar загружается первым. В этом фрагменте s.async = false не блокирует анализатор во время загрузки сценариев, потому что сценарии добавляются динамически. Сигнал останавливается только во время выполнения сценариев, как и для async сценариев. Однако, с этим фрагментом, имейте в виду:

  • Один или оба сценария могут выполнить до того, как документ закончил загрузку. Если вы хотите, чтобы документ был готов к моменту выполнения сценариев, дождитесь загруженного события DOMContentLoaded прежде чем добавить сценарии. Если это вызывает проблему с производительностью, потому что сценарии не начинают загружать достаточно рано, используйте теги предварительной загрузки ранее на странице.
  • defer = true ничего не делает. Если вам нужно такое поведение, запустите сценарий вручную, когда это необходимо.

Шаг 3: Рефактор HTML-шаблоны и код на стороне клиента

Встроенные обработчики событий (например, onclick="…" , onerror="…" ) и JavaScript Uris ( <a href="javascript:…"> ) могут использоваться для запуска сценариев. Это означает, что злоумышленник, который считает ошибку XSS, может внедрить этот вид HTML и выполнить вредоносный JavaScript. CSP, не являющийся хеш-на основе, запрещает использование такого рода разметки. Если ваш сайт использует какую -либо из этих моделей, вам нужно рефактировать их в более безопасные альтернативы.

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

Отчеты о нарушении CSP в консоли разработчика Chrome.
Комплексные ошибки для заблокированного кода.

В большинстве случаев исправление простое:

Рефакторные обработчики мероприятий

Разрешен CSP
<span id="things">A thing.</span>
<script nonce="${nonce}">
  document.getElementById('things').addEventListener('click', doThings);
</script>
CSP позволяет обработчикам событий, которые зарегистрированы с использованием JavaScript.
Заблокирован CSP
<span onclick="doThings();">A thing.</span>
CSP блокируют встроенные обработчики событий.

Refactor javascript: Uris

Разрешен CSP
<a id="foo">foo</a>
<script nonce="${nonce}">
  document.getElementById('foo').addEventListener('click', linkClicked);
</script>
CSP позволяет обработчикам событий, которые зарегистрированы с использованием JavaScript.
Заблокирован CSP
<a href="javascript:linkClicked()">foo</a>
CSP блокирует JavaScript: Uris.

Удалить eval() из JavaScript

Если ваше приложение использует eval() для преобразования сериализаций JSON String в объекты JS, вы должны рефакторировать такие экземпляры в JSON.parse() , что также быстрее .

Если вы не можете удалить все использование eval() , вы все равно можете установить строгий CSP, не основанный на нере, но вы должны использовать 'unsafe-eval' , которое делает вашу политику немного менее безопасной.

Вы можете найти это и больше примеров такого рефакторинга в этом строгом CSP CodeLab:

Шаг 4 (необязательно): Добавьте запасные отступления, чтобы поддержать старые версии браузера

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

  • Хром: 52.
  • Край: 79.
  • Firefox: 52.
  • Сафари: 15.4.

Источник

Если вам нужно поддержать старые версии браузера:

  • Использование strict-dynamic требует добавления https: в качестве запасного для более ранних версий Safari. Когда вы делаете это:
    • Все браузеры, которые поддерживают strict-dynamic игнорируют https: MANKBACK, так что это не уменьшит силу политики.
    • В старых браузерах сценарии с внешним источником могут загружаться, только если они поступают из HTTPS Origin. Это менее безопасно, чем строгий CSP, но все же предотвращает некоторые общие XSS, такие как инъекции javascript: URIS.
  • Чтобы обеспечить совместимость с очень старыми версиями браузера (4+ года), вы можете добавить unsafe-inline в качестве запасного. Все недавние браузеры игнорируют unsafe-inline если присутствует NOCE CSP или хэш.
Content-Security-Policy:
  script-src 'nonce-{random}' 'strict-dynamic' https: 'unsafe-inline';
  object-src 'none';
  base-uri 'none';

Шаг 5: развернуть свой CSP

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

  1. (Необязательно) Разверните свой CSP в режиме только для сообщений, используя Content-Security-Policy-Report-Only . Режим только для отчета удобен для проверки потенциально нарушающего изменения, таких как новая CSP в производстве, прежде чем вы начнете обеспечивать соблюдение ограничений CSP. В режиме только в счете ваш CSP не влияет на поведение вашего приложения, но браузер все еще генерирует ошибки консоли и отчеты о нарушении, когда он сталкивается с шаблонами, несовместимыми с вашим CSP, поэтому вы можете увидеть, что бы сломало бы для ваших конечных пользователей. Для получения дополнительной информации см. Отчеты API .
  2. Когда вы уверены, что ваш CSP не сломает ваш сайт для ваших конечных пользователей, разверните свой CSP, используя заголовок ответа Content-Security-Policy . Мы рекомендуем установить ваш CSP с помощью сервера HTTP Hearer Server, потому что он более безопасен, чем тег <meta> . После завершения этого шага ваш CSP начинает защищать ваше приложение от XSS.

Ограничения

Строгий CSP, как правило, обеспечивает сильный дополнительный уровень безопасности, который помогает смягчить XSS. В большинстве случаев CSP значительно снижает поверхность атаки, отвергая опасные паттерны, такие как javascript: URIS. However, based on the type of CSP you're using (nonces, hashes, with or without 'strict-dynamic' ), there are cases where CSP doesn't protect your app as well:

  • If you nonce a script, but there's an injection directly into the body or the src parameter of that <script> element.
  • If there are injections into the locations of dynamically created scripts ( document.createElement('script') ), including into any library functions that create script DOM nodes based on the values of their arguments. This includes some common APIs such as jQuery's .html() , as well as .get() and .post() in jQuery < 3.0.
  • If there are template injections in old AngularJS applications. An attacker that can inject into an AngularJS template can use it to execute arbitrary JavaScript .
  • If the policy contains 'unsafe-eval' , injections into eval() , setTimeout() , and a few other rarely used APIs.

Developers and security engineers should pay particular attention to such patterns during code reviews and security audits. You can find more details on these cases in Content Security Policy: A Successful Mess Between Hardening and Mitigation .

Further reading