CSP политики и Stimulus

Content Security Policy (CSP) — это механизм безопасности веб-приложений, предназначенный для предотвращения атак типа XSS (Cross-Site Scripting) и других инъекций. CSP позволяет строго ограничить источники загрузки скриптов, стилей, медиафайлов и других ресурсов, что особенно важно при работе с современными JavaScript-фреймворками, включая Stimulus.

Stimulus — это легковесный фреймворк, ориентированный на управление поведением HTML через контроллеры, связывая действия с элементами DOM без необходимости полного SPA-подхода. Его интеграция с CSP требует понимания того, как фреймворк генерирует и выполняет код.


Основные принципы работы Stimulus

Stimulus работает по паттерну HTML+JavaScript:

  • Контроллеры объявляются в виде ES6-классов.
  • Элементы DOM связываются с контроллерами через data-controller.
  • Действия и значения передаются через атрибуты data-action и data-<controller>-target.

Пример базового контроллера:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["output"]

  greet() {
    this.outputTarget.textContent = "Привет, мир!"
  }
}

HTML для привязки:

<div data-controller="hello">
  <button data-action="click->hello#greet">Сказать привет</button>
  <span data-hello-target="output"></span>
</div>

Stimulus не требует инлайн-скриптов, что делает его совместимым с строгими CSP-настройками.


CSP и inline-скрипты

CSP по умолчанию блокирует inline-скрипты (<script> без src) и inline-события (onclick, onchange). Stimulus полностью строится на декларативной привязке через атрибуты, что избавляет от необходимости использовать инлайн-обработчики.

Пример CSP-заголовка:

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

Здесь ключевым моментом является исключение unsafe-inline для скриптов, так как Stimulus использует модули ES и внешние файлы, а не инлайн-код. Это повышает безопасность, исключая возможность выполнения непроверенного JavaScript.


Использование Stimulus с модулями и CSP

Stimulus рекомендуется подключать через ES-модули или сборщик (Webpack, Vite). CSP требует явного указания источников:

<script type="module" src="/js/controllers/index.js"></script>

CSP заголовок для модулей:

Content-Security-Policy:
  script-src 'self' 'strict-dynamic' https://cdn.jsdelivr.net;
  object-src 'none';

Использование type="module" автоматически исключает необходимость разрешать inline-скрипты.


Проблемы с eval и dynamic code

Некоторые библиотеки используют eval или Function для динамического создания кода. Stimulus не применяет eval и не создаёт код динамически, что делает его полностью совместимым с CSP, запрещающим 'unsafe-eval':

Content-Security-Policy:
  script-src 'self';
  style-src 'self';
  object-src 'none';

Таким образом, можно полностью запретить динамическое выполнение кода без потери функциональности Stimulus.


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

Stimulus поддерживает связывание событий через data-action. Даже при генерации элементов через Jav * aScript:

const button = document.createElement("button")
button.dataset.action = "click->hello#greet"
button.textContent = "Сказать привет"
document.body.appendChild(button)

CSP не блокирует такие действия, так как это не inline-скрипт и не использование eval.


Подключение внешних библиотек с CSP

Если Stimulus-контроллеры используют внешние библиотеки, необходимо явно разрешить их источники:

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

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


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

  • Всегда использовать type="module" для контроллеров.
  • Исключить unsafe-inline для скриптов.
  • Не использовать инлайн-обработчики (onclick и т.д.).
  • При необходимости подключать внешние библиотеки, добавлять их домены в script-src.
  • Для стилей разрешается 'unsafe-inline' только при использовании inline-стилей, иначе лучше подключать через CSS-файлы.

Интеграция Stimulus с CSP в Rails

В Rails 7 с Hotwire и Stimulus CSP поддерживается по умолчанию:

  • Контроллеры хранятся в app/javascript/controllers.
  • Используется Webpacker или Import Maps.
  • CSP заголовки задаются в config/initializers/content_security_policy.rb.

Пример:

Rails.application.config.content_security_policy do |policy|
  policy.default_src :self
  policy.script_src  :self, :https
  policy.style_src   :self, :https
end

Stimulus-контроллеры полностью работают с такой конфигурацией без использования inline-скриптов.


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