Content Security Policy

Content Security Policy (CSP) представляет собой механизм безопасности веб-приложений, который контролирует, какие ресурсы могут быть загружены и выполнены на странице. Для приложений на Riot.js это особенно важно, так как фреймворк активно использует динамическое создание компонентов и вставку HTML через шаблоны. Неправильно настроенная CSP может блокировать выполнение скриптов, стилей или inline-шаблонов компонентов.


Основы CSP

CSP формируется с помощью HTTP-заголовка Content-Security-Policy. Его основная цель — ограничить источники контента:

  • script-src — источники JavaScript.
  • style-src — источники CSS.
  • img-src — источники изображений.
  • connect-src — разрешённые URL для AJAX-запросов.
  • default-src — базовое ограничение для всех типов ресурсов, если они явно не указаны.

Для Riot.js важен параметр script-src, так как большинство компонентов компилируется в JavaScript, который выполняется в браузере. Строгие политики могут блокировать inline-скрипты, используемые в Riot.js-компонентах.


Особенности работы Riot.js с CSP

Riot.js поддерживает два основных подхода к использованию компонентов:

  1. Inline-компоненты: код компонента описан прямо в HTML-файле через тег <script type="riot">.
  2. Компилированные компоненты: компоненты собираются на этапе сборки (с помощью @riot/compiler) в чистый JavaScript.

Inline-компоненты и CSP

Inline-компоненты создают трудности с CSP, так как политика по умолчанию запрещает выполнение inline-скриптов ('unsafe-inline'). Для их работы требуется одна из следующих стратегий:

  • Использование nonce-токенов:
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'nonce-xyz';">
<script type="riot" nonce="xyz">
  <my-component>
    <p>Hello World</p>
  </my-component>
</script>

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

  • Добавление 'unsafe-inline' в script-src:
Content-Security-Policy: script-src 'self' 'unsafe-inline';

Этот подход менее безопасен, но прост для разработки и прототипирования.

Компилированные компоненты и CSP

Компилированные компоненты полностью устраняют необходимость inline-скриптов. В этом случае CSP может быть строгой:

Content-Security-Policy: default-src 'self'; script-src 'self';

Все компоненты подключаются как внешние файлы .js, что соответствует безопасной политике.


CSP и динамическая вставка HTML

Riot.js активно использует синтаксис { expression } и ref для рендеринга контента внутри шаблонов. Любые динамические вставки HTML (innerHTML) могут быть заблокированы строгой CSP. Решения:

  • Использование функций Riot.js для рендеринга данных без прямой вставки HTML:
<p>{ item.name }</p>
  • Избегание прямого присваивания HTML через innerHTML, если политика CSP запрещает 'unsafe-inline'.

Настройка CSP для AJAX-запросов

Большинство приложений на Riot.js используют AJAX для загрузки данных (fetch, axios). Необходимо явно разрешать домены, к которым будут обращаться запросы, через connect-src:

Content-Security-Policy: connect-src 'self' https://api.example.com;

Это предотвращает блокировку запросов браузером и сохраняет безопасность.


CSP и внешние библиотеки

Использование внешних библиотек (например, CDN для стилей или скриптов) требует явного указания источников:

Content-Security-Policy: 
  script-src 'self' https://cdn.jsdelivr.net;
  style-src 'self' https://cdn.jsdelivr.net;

Для Riot.js это актуально при подключении UI-библиотек или сторонних компонентов, чтобы их скрипты не блокировались политикой.


Практические рекомендации

  • Предпочтение компилированным компонентам. Они полностью совместимы со строгой CSP.
  • Использование nonce для inline-скриптов, если требуется динамическая вставка компонентов в HTML.
  • Явное указание источников ресурсов (script-src, style-src, connect-src) для предотвращения блокировки критических функций приложения.
  • Избегание innerHTML и eval — такие методы не поддерживаются в безопасных CSP и создают риски XSS.
  • Тестирование CSP в разных браузерах, так как поддержка директив может отличаться, особенно для nonce и hash в старых версиях браузеров.

CSP и hot-reload при разработке

При локальной разработке с использованием vite или webpack может потребоваться более либеральная политика CSP, чтобы поддерживать hot-reload:

Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval';

Для продакшн-сборки необходимо убрать 'unsafe-inline' и 'unsafe-eval', чтобы соответствовать стандартам безопасности.


Настройка Content Security Policy является критически важным элементом безопасности для Riot.js приложений. Компилированные компоненты и правильное управление источниками скриптов и стилей позволяют сохранять строгую политику безопасности без потери функциональности.