В настоящее время я работаю над улучшением безопасности сайта Drupal 8 путем внедрения политики безопасности контента. Поскольку это все еще новое для меня, я хотел бы внести некоторые изменения в свою стратегию.
Соответствующая базовая настройка
- Drupal 8 со специальным патчем, который делает
ckeditor.js
передавать одноразовые номера при загрузке своих плагинов, CKEditor используется только зарегистрированными пользователями
- Комплект безопасности, исправленный, чтобы предоставить перехватчик изменений для директив CSP (https://www.drupal.org/project/seckit/issues/2844205#comment-14217383).
- Пользовательский код для внесения изменений в отображаемый HTML и конфигурацию CSP из модуля Security Kit.
Предварительные соображения
Я хотел бы не использовать 'небезопасно встроенный'
для скриптов, если это возможно, но в конце концов я понял, что это будет работать только для определенных браузеров.
Я также хотел бы иметь адекватный CSP для разных ситуаций (разные браузеры, вход в систему и не вход в систему).
Это приводит меня к этой идее для сценарий-источник
:
- с использованием
одноразовый номер
и «строгий динамический»
для браузеров, поддерживающих CSP v3 и некэшируемые страницы
- использование хэшей для браузеров, поддерживающих CSP v3 и кешируемые страницы
- с использованием
'небезопасно встроенный'
вместе со списком на основе домена для всех других браузеров
Основная причина различия между браузерами заключается в том, что я не смог найти способ создания директив CSP с обратной совместимостью без необоснованного количества изменений в модулях ядра и дополнительных модулях.
Что я сделал до сих пор на моем местном тестовом сайте
Я добавил логику, которая использует одноразовый номер
и «строгий динамический»
для браузеров на основе Chrome для зарегистрированных пользователей, для которых страницы не кэшируются, гарантируя, что одноразовые номера являются новыми и уникальными для каждого запроса. Эта логика основана на строке агента пользователя (я знаю, что это небезопасно, но лучшего решения я не вижу).
Таким образом, в основном, для браузера на основе Chrome, вошедшие в систему пользователи получат заголовок CSP с этими политиками CSP, связанными со сценариями ('небезопасно встроенный'
в script-src будет игнорироваться браузерами, поддерживающими 'строгий-динамический'
):
script-src 'self' 'unsafe-inline' *.googletagmanager.com *.google-analytics.com 'nonce-SECURENONCE' 'strict-dynamic';
script-src-attr 'небезопасный встроенный';
Анонимные пользователи получат CSP, который выглядит примерно так:
script-src 'self' 'unsafe-inline' *.googletagmanager.com *.google-analytics.com 'sha256-HASH_1' 'sha256-HASH_2' 'sha256-HASH_3' 'sha256-HASH_4' 'sha256-HASH_5' .. .;
script-src-attr 'небезопасный встроенный';
Браузеры, не основанные на Chrome, получают заголовок CSP, который выглядит следующим образом:
script-src 'self' 'unsafe-inline' *.google-analytics.com *.googletagmanager.com;
Я также добавил логику, которая, основываясь на вышеуказанных критериях выбора, добавляет либо одноразовый номер
атрибуты для каждого тега скрипта (некэшированные страницы) или хэш-коды для каждого тега скрипта (кэшированные страницы). Это также позволяет мне нормально работать с CKEditor в бэкэнде.
Кажется, он хорошо работает в разных браузерах, которые я тестировал: Brave, Chrome, Edge, Firefox и Safari. Только у трех первых есть CSP, который я считаю безопасным (также проверено с помощью https://csp-evaluator.withgoogle.com/).
Является ли допустимым подходом иметь:
- разные CSP для зарегистрированных и анонимных пользователей?
- разные CSP для разных браузеров (или на самом деле разные «сообщенные браузеры»)?