Наше основное приложение https://app.optimizely.com теперь защищено Политикой безопасности контента (CSP). Благодаря этому изменению наши пользователи защищены от атак межсайтового скриптинга (XSS), которые OWASP называет наиболее распространенной уязвимостью безопасности веб-приложений; для более подробного обзора ознакомьтесь с книгой Майка Уэста Введение в CSP. Прошло чуть больше года с того момента, когда мы впервые развернули CSP в режиме Report-Only, до того момента, когда мы смогли применить политику. Если вы подумываете о развертывании CSP на своем веб-сайте, мы надеемся, что этот пост предоставит некоторую полезную информацию, которая поможет вам спланировать развертывание.

Прежде чем вдаваться в подробности, немного контекста: наше приложение сильно зависит от JavaScript. Он загружает сторонние сайты в редактор WYSIWYG, который позволяет нетехническим пользователям создавать варианты своих сайтов. В дополнение к этому, существуют десятки других рабочих процессов, облегчающих веб-эксперименты как для нетехнических, так и для технических пользователей. Прекращение межсайтового скриптинга и внедрения контента имеет для нас первостепенное значение.

Отчетность

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

Важной функцией, связанной с отчетностью, является функция CSP только для отчетов. В этом режиме заголовок CSP, обычно установленный как Content-Security-Policy, изменяется на Content-Security-Policy-Report-Only, и этот заголовок указывает браузеру сообщать о нарушения политики, но фактически не обеспечивают их соблюдение.

Когда браузер обнаруживает нарушение политики, он создает отчет о нарушении и кодирует его в JSON. В приведенном ниже примере отчета об ошибке, скопированном из Спецификации CSP уровня 2, была предпринята попытка загрузки изображения с evil.example.com и заблокирована из-за директивы default-src (которая распространяется на img- src, если не указана директива img-src).

{
«csp-report»: {
«document-uri»: «http://example.org/page.html,
« referrer »:« http: // evil. example.com/haxor.html,
«blocked-uri»: «http://evil.example.com/image.png,
« violated-directive »:« default-src 'self' »,
« эффективная-директива »:« img-src »,
« исходная-политика »:« default-src 'self'; report-uri http://example.org/csp-report.cgi
}
}

В Документации CSP W3C описывается определение report-uri, который будет получать эти отчеты о нарушении политики при нарушении политики, определенной веб-сайтами. Последняя итерация спецификации CSP определяет директиву report-to, которая предназначена для замены report-uri. Формат report-to определен в Reporting API, в настоящее время это черновик W3C, предназначенный для определения общей структуры отчетов об ошибках для CSP, сетевых ошибок и других предстоящих видов ошибок. Я еще не встречал другого веб-ресурса, который использует отправку отчетов. Однако, как и многие стандарты, потребуется некоторое время, чтобы попасть в Интернет, особенно с учетом того, что CSP уровня 3 и Reporting API все еще находятся в стадии черновика.

Мы написали собственный сервер отчетов CSP, так как не было доступных решений, которые бы соответствовали нашим потребностям. Этот бэкэнд принимает полезные данные JSON, как указано выше, но с некоторыми дополнительными метаданными:

  • UserAgent
  • Дорожка
  • Имя браузера
  • Версия браузера
  • OS
  • Отметка времени

Можно протащить еще больше информации из браузера отчетов, добавив поля в строку запроса report-uri, как это было предложено Twitter в их дизайне сборщика отчетов, но мы не нашли этого необходимо. Самой важной для нас информацией было знание того, какой путь, браузер и операционная система столкнулись с нарушением политики.

Обратите внимание, что сейчас существует стартап www.templarbit.com, который предоставляет бэкенды отчетов CSP, так что вам, возможно, не придется писать свои собственные.

Второй уровень

Изначально мы развернули политику соответствия CSP Level 2. Эта политика внесла в белый список все одобренные источники с помощью следующих директив:

  • скрипт-src
  • style-src
  • img-src
  • font-src

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

С политикой, основанной на белом списке происхождения, мы постоянно играли в догонялки с изменениями, внесенными командой инженеров. В самом деле, не только сложно поддерживать политику белых списков, но и чертовски сложно написать безопасную политику. Это почему?

Введите строгую динамику

В конце 2016 года исследование Google опубликовало CSP Is Dead, Long Live CSP! О незащищенности белых списков и будущем политики безопасности контента . В нем авторы приходят к ужасному выводу относительно белых списков CSP:

В целом, мы обнаружили, что 94,68% политик, которые пытаются ограничить выполнение скриптов, неэффективны, и что 99,34% хостов с CSP используют политики, которые не дают преимуществ перед XSS.

После анализа 26011 уникальных белых списков CSP он продолжает:

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

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

С момента своего появления в CSP уровня 2 криптографические одноразовые номера предложили интригующую альтернативу занесению в белый список отдельных источников происхождения: содержимое данного элемента ‹script› авторизовано для выполнения, если элемент включает одноразовый атрибут атрибута, значение которого соответствует одноразовый номер, указанный в HTTP-ответе, обслуживающем страницу. Если одноразовые значения не совпадают (или отсутствуют оба), сценарий не будет выполняться.

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

Для сайтов с большой кодовой базой JavaScript это огромный барьер для принятия. CSP Level 3 решает эту проблему с помощью ключевого слова strict-dynamic. Это ключевое слово позволяет динамически сгенерированным сценариям наследовать одноразовый номер от доверенного сценария, который его создал. Злоумышленники, обнаружившие уязвимость к внедрению, будут заблокированы внедренным скриптом, поскольку они знают правильный одноразовый номер. strict-nonce существенно сокращает объем необходимого рефакторинга JavaScript и позволяет сайтам с большой существующей кодовой базой, такой как наша, использовать CSP.

Рефакторинг

Добавить заголовок CSP и добавить одноразовый номер ко всем тегам ‹script› несложно. Просто добавьте ‹script nonce =” 0123456789 ”›, где 0123456789 - случайный одноразовый номер, соответствующий одноразовому запросу в заголовке HTTP CSP. Сложность заключается в отслеживании функций, которые не часто используются и не имеют полного покрытия автоматизации тестирования. Вот где решающее значение имеет инфраструктура отчетности.

Первая строка

Одно из самых неприятных нарушений CSP, которые необходимо отслеживать, - это те, при которых сообщенная ошибка сценария отображается как / SomeResource: 1 или имеет значение self.

Вышеупомянутое нарушение CSP связано с кликабельным элементом, который не реагирует должным образом при нажатии. Однако при нажатии на источник ошибки 8519510174: 1 в браузере мы просто перемещаемся в верхнюю часть HTML-кода страницы в консоли браузера - это не особенно полезно. Наблюдение за этой ошибкой в ​​серверной части с помощью механизма отчетов об ошибках CSP также расстраивает, поскольку невозможно определить причину ошибки.

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

Для некоторых проблем для document-uri установлено значение «data: text / html, chromewebdata» в Chrome. Как полезно.

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

Каждое новое предупреждение необходимо исследовать и воспроизводить. Только тогда можно определить, ложно это или нет. Также будут проблемы, вызванные программным обеспечением на стороне клиента, которые не могут быть исправлены, но должны быть приняты. В средах настольных компьютеров как в управляемых средах конечных пользователей, так и в корпоративных средах обычно есть программное обеспечение, которое проверяет поведение программ и сети. Это может повлиять на браузеры и их взаимодействие с вашим сайтом. Например:

  • blocked-uri = https://gc.kis.v2.scr.kaspersky-labs.com/6BBC944A-119C-4842-B955-11A953814FFE/main.js. Это означает, что Kaspersky, поставщик антивирусного программного обеспечения для настольных ПК, внедряет собственный Javascript на все страницы, которые просматривает пользователь. Несмотря на то, что мы доставляем наш сайт исключительно через HTTPS, мы не можем контролировать, какое программное обеспечение на стороне клиента может перехватывать и изменять наши HTTP-ответы до того, как они в конечном итоге будут использованы браузером конечного пользователя. Такие случаи, как этот -, когда клиентское программное обеспечение переписывает нашу страницу, могут быть исправлены только на клиенте, что мы не контролируем.
  • source-file = «safari-extension: //com.lastpass.lpsafariextension-n24rep3bmn». Это подчеркивает еще одну проблему: плагины браузера могут генерировать предупреждения. В этом случае расширение LastPass Safari. Подобно локальному программному обеспечению, которое проверяет и иногда перезаписывает наши страницы, некоторые плагины браузера выполняют аналогичные действия.

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

Тестирование

Optimizely полагается на автоматизированные тесты, которые выполняются при каждом изменении кода, чтобы предотвратить регресс. Чтобы изменения внешнего интерфейса не приводили к регрессу, мы полагаемся на BrowserStack для выполнения нашего набора внешних тестов и отправки отчетов об ошибках обратно в наши сборки. Более старая версия драйверов селена, используемых BrowserStack и аналогичными поставщиками тестирования, полностью отключила CSP; К счастью, новейшие драйверы селена Chrome поддерживают CSP. Вы можете адаптировать следующий тест, чтобы определить, поддерживает ли данный драйвер Selenium CSP:

Https://github.com/ooola/selenium-csp

К сожалению, поставщики средств автоматизации тестирования браузеров плохо документируют, какие функции безопасности HTTP они отключают или настраивают.

Сторонний JavaScript

Если ваш сайт содержит сторонний JavaScript, как это делаем мы, вам может потребоваться поработать с ними, чтобы убедиться, что их скрипты совместимы с вашей политикой. В частности, их встроенные обработчики событий должны быть подвергнуты рефакторингу, если они написаны несовместимым с CSP способом. Для получения дополнительной информации посетите csp.withgoogle.com.

Наша политика

Заголовок Content-Security-Policy выглядит следующим образом:

script-src http: https: ‘self’ ‘unsafe-inline’ ‘unsafe-eval’ ‘strict-dynamic’ ‘nonce-I8yxR7k / AuBxOXbiMJSF7g ==’; img-src data: http: https :; frame-ancestors https://app.optimizely.com http: // localhost: 8000 https://app.experimentengine.com https://app-staging.experimentengine.com https: //demo.experimentengine.com https://teams.optimizely.com; plugin-types application / x-shockwave-flash application / pdf; object-src https://app.optimizely.com/static/includes/swf/ZeroClipboard.swf; base-uri "сам"; report-uri https://cspreporter.optimizely.com/report/999f2de5-b04d-4544-95c6-39705e57da35;

При подключении этой политики к оценщику CSP Google будет выделено ключевое слово ‘unsafe-eval’, которое требуется нашим инструментам редактирования сайта. Точно так же он не одобряет загрузку ZeroClipboard, которая необходима для копирования / вставки между веб-приложением Optimizely и рабочим столом пользователя. Предки фреймов позволяют разместить наш сайт во фреймах благодаря нашему недавнему приобретению Experiment Engine.

Эта политика очень похожа на другие политики на основе CSP уровня 3, которые полагаются на ключевое слово strict-dynamic. В качестве другого примера взгляните на заголовок CSP, установленный console.cloud.google.com.

Развертывание

Реализовать политику, совместимую с CSP уровня 3, просто, даже если она охватывает большое приложение JavaScript, использующее менее часто используемые части языка и веб-API. Изучение выявленных проблем и переписывание затронутого кода JavaScript для обеспечения совместимости с политиками может выполняться по одной проблеме за раз. На это у нас ушла первая половина этого года.

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

Заключение

Предотвращение внедрения контента и XSS становится все более важным, поскольку все более важные бизнес-функции перемещаются в Интернет. Если вы подумываете о включении CSP в свое веб-приложение, мы настоятельно рекомендуем вам начать экспериментировать с простой политикой, совместимой с уровнем 3 только для отчетов. С помощью только для отчетов вы можете исправлять нарушения политики так, как вы их видите, в удобное для вас время. Вскоре вы тоже сможете обеспечить соблюдение своей политики и сделать свое веб-приложение на несколько порядков менее восприимчивым к XSS.

Дополнительная информация

Если у вас есть время, рекомендую прочитать CSP is Dead, Long Live CSP - оно того стоит!

"Мы нанимаем!"

-Ола