В InVision мы недавно опубликовали Политику безопасности контента (CSP) для наших веб-приложений.

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

Настройка политики безопасности контента включает в себя создание списка ресурсов, к которым пользовательскому агенту разрешено подключаться или загружать для страницы, таких как JavaScript, CSS, шрифты и фреймы. Этот список должен быть добавлен либо в HTTP-заголовок Content-Security-Policy, либо в <meta http-equiv="Content-Security-Policy">.

Затем браузер будет сверяться с политикой каждый раз, когда ему нужно запросить ресурс, чтобы определить, разрешено ли ему загружать его. Если ресурс не может быть загружен, браузер выдаст SecurityPolicyViolationEvent, который вы можете собрать и отправить агрегатору нарушений (например, Sentry или Report-URI) для дальнейшего анализа.

Добавление политики безопасности контента на веб-страницу — отличный способ повысить ее безопасность, но это еще не все.

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

В этом посте мы хотим выделить несколько проблем, которые замедлили развертывание CSP.

Кроссбраузерные несоответствия

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

Большинство этих различий незначительны, но некоторые могут затруднить понимание того, почему происходит конкретное нарушение.

Чтобы воспроизвести эти несоответствия в вашем браузере, мы создали пример HTML-файла со следующим CSP:

script-src 'unsafe-inline';

Этот CSP допускает только встроенные ресурсы, такие как встроенные элементы <script>, встроенные обработчики событий и встроенные элементы <style>.
В этом примере мы загружаем внешний скрипт (JQuery), чтобы вызвать SecurityPolicyViolationEvent.

Основные отличия от события нарушения, которые мы замечаем:

  • violationEvent.effectiveDirective: это директива, исполнение которой выявило нарушение.
    В Firefox и Safari это директива политики, которая была нарушена. В примере установлено значение script-src.
    В Chrome это будет самая «конкретная» директива, которая была нарушена. В примере это script-src-elem — даже если мы не объявили эту директиву — потому что именно здесь произошло бы нарушение, если бы такая директива присутствовала в политике.
  • violationEvent.blockedURI : это URI ресурса, который был заблокирован из-за нарушения политики.
    В Chrome и Firefox это полный URI ресурса. В примере это https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js.
    В Safari это просто источник ресурса. В примере это https://ajax.googleapis.com.
  • violationEvent.violatedDirective: это директива, исполнение которой выявило нарушение. Согласно спецификации CSP3, это копия свойства effective-directive, сохраненная по историческим причинам.
    В Chrome и Firefox эта директива политики была нарушена. В примере это script-src.
    В Safari это директива и значение, которое было нарушено. В примере это script-src 'unsafe-inline'.

Safari и директива base-uri

Еще одно незначительное несоответствие браузера, которое мы обнаружили, заключается в том, что в Safari удаление элемента <base> со страницы с директивой CSP base-uri вызывает нарушение CSP — независимо от значения, установленного в base-uri.

Директива base-uri ограничивает URL-адреса, которые можно использовать для указания базового URL-адреса документа.

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

Исходя из нашего понимания спецификации CSP и алгоритма, определенного в спецификации HTML5 для получения базы документа, проблема должна быть на стороне Safari: если на странице нет доступного элемента <base>, база документа должна вернуться к URL-адресу документа. (вместо того, чтобы бросать нарушение).

wasm-eval нарушение

Мы заметили, что Chrome сообщает о запутанной ошибке на страницах, использующих WebAssembly с включенным CSP.

На сегодняшний день единственный способ заставить WebAssembly работать с CSP — это добавить script-src unsafe-eval в политику — по крайней мере, до тех пор, пока не будет доступен лучший, специфичный для wasm вариант (wasm-eval).

Тем не менее, даже если включено предложение unsafe-eval, Chrome сообщает о следующей ошибке:

[Только отчет] Отказано в компиляции или создании экземпляра модуля WebAssembly, поскольку «wasm-eval» не является разрешенным источником скрипта в следующей директиве Content Security Policy: «script-src 'self' 'unsafe-inline' 'unsafe- оценка

Эта ошибка была очень запутанной, поскольку не означает, что Chrome отказался компилировать или создавать экземпляр модуля WebAssembly — это просто предупреждение, вызванное тем, что пункт wasm-eval пока не поддерживается Chrome.

Нарушения для ресурсов, которые правильно заданы в политике

Это была самая трудоемкая проблема, которую нам приходилось отлаживать.

Проверив отчеты о нарушениях CSP, мы заметили, что многие из них относятся к ресурсам, которые мы фактически разрешили в нашем CSP.

Например, у нас было несколько нарушений ресурса https://px.ads.linkedin.com (используемого пиксельным трекером LinkedIn), даже если он был явно разрешен в нашем списке.

К сожалению, нам не удалось воспроизвести ни одно из этих нарушений, а дополнительные метаданные нарушения (user-agent, браузер, местоположение и т. д.) не намекали на какую-либо общую причину.

Сначала мы думали, что эти нарушения были вызваны расширениями браузера (например, блокировщиками рекламы), которые применяют более строгий CSP для блокировки некоторых ресурсов во время выполнения. Мы начали вручную тестировать бесчисленное количество браузерных расширений, но ни разу не смогли воспроизвести эти нарушения — все браузерные расширения блокировали ресурсы на уровне сетевого подключения.

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

Через несколько дней наше терпение окупилось!

Мы получили отчет о нарушении, исходящем от браузера инженера InVision, поэтому мы объединились и, наконец, обнаружили основную причину проблемы: редиректы!

Это была переадресация! В случае нарушений, вызванных перенаправлениями, браузеры сообщают в blocked-uri начальный URI цепочки перенаправлений.

В браузере этого инженера InVision URL-адрес https://px.ads.linkedin.com перенаправлялся на https://px4.ads.linkedin.com (по данным LinkedIn сервера).
Поскольку наша политика допускает https://px.ads.linkedin.com (но не px4), сгенерированное нарушение имело https://px.ads.linkedin.com в качестве URI, даже если нарушенный URI был https://px4.ads.linkedin.com.

Такое поведение еще больше усложняет отладку нарушений, потому что у нас нет подробностей о том, какой целевой URI цепочки перенаправления фактически нарушил CSP — мы получаем только исходный URI, который уже разрешен. Усложняет это то, что это обычно вызвано сервером ресурсов, обработчиками границ, прокси-серверами и т. д. по многим причинам, таким как геолокация, проблемы с сервером, устаревшие API и т. д., что делает их очень громоздкими для прогнозирования и воспроизведения без знания причина переадресации.

Помимо проблем с перенаправлением на разные источники, мы также заметили, что нарушение может быть вызвано несоответствием типа контента между ожидаемым типом ресурса и его полученным типом.
Например, если сетевой запрос сценария возвращает результат HTML 404/500, браузер инициирует нарушение без какой-либо информации, намекающей на несоответствие типа контента.

На данный момент кажется, что мы практически ничего не можем сделать, чтобы упростить процесс отладки, и чтобы понять это, нужно много погружаться. Мы не смогли найти много помощи в Интернете по этим вопросам во время нашего путешествия, поэтому мы надеемся, что этот пост поможет другим командам в их путешествии по CSP!

Если вы заинтересованы в том, чтобы помочь решить эти захватывающие задачи, мы всегда нанимаем!