Content Security Policy и сервис-воркер

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

Workbox — это набор библиотек от Google, упрощающий создание прогрессивных веб-приложений (PWA) с сервис-воркерами. Он автоматизирует кеширование ресурсов, стратегий сетевого взаимодействия и управление версиями файлов. При интеграции с CSP необходимо учитывать несколько ключевых аспектов.


Ограничения CSP для сервис-воркеров

  1. Запрет inline-скриптов CSP обычно блокирует inline-скрипты ('unsafe-inline'), а также использование eval(). Workbox в стандартной конфигурации может генерировать inline-скрипты при автоматическом добавлении precaching-манифеста или динамических обработчиков. Для корректной работы необходимо использовать внешние файлы скриптов и избегать inline-кода в сервис-воркере.

  2. Разрешение источников Политика script-src должна включать домены, с которых загружаются скрипты воркера. В случае Workbox это чаще всего локальные скрипты или файлы, сгенерированные инструментами сборки (workbox-vX.X.X/workbox-sw.js). CSP-пример для Workbox:

    Content-Security-Policy:
      default-src 'self';
      script-src 'self' https://cdn.jsdelivr.net/npm/workbox-cdn@6.5.4/workbox-sw.js;
      connect-src 'self' https:;
      object-src 'none';
      frame-ancestors 'none';
  3. Запрет eval() и new Function() Некоторые методы Workbox, например workbox-build для генерации precache manifest, используют динамическое создание функций. В продуктивной сборке необходимо транспилировать и бандлить скрипты так, чтобы не использовать eval().


Настройка CSP для Workbox

  1. Выделение воркера в отдельный файл Сервис-воркер должен регистрироваться как отдельный JS-файл. Пример регистрации:

    if ('serviceWorker' in navigator) {
      window.addEventListener('load', () => {
        navigator.serviceWorker.register('/sw.js')
          .then(registration => {
            console.log('SW зарегистрирован:', registration);
          })
          .catch(error => {
            console.error('Ошибка регистрации SW:', error);
          });
      });
    }

    CSP не будет блокировать загрузку файла, если он размещён на том же домене ('self') или явно разрешён в script-src.

  2. Внешние библиотеки Workbox Workbox можно подключить через CDN или включить в сборку. Использование CDN требует явного разрешения домена в script-src. Рекомендуется вариант с локальным бандлом для упрощения CSP и повышения безопасности.

  3. Precache и runtime caching Примеры конфигурации кеширования через Workbox:

    import {precacheAndRoute} from 'workbox-precaching';
    import {registerRoute} from 'workbox-routing';
    import {StaleWhileRevalidate} from 'workbox-strategies';
    
    precacheAndRoute(self.__WB_MANIFEST);
    
    registerRoute(
      ({request}) => request.destination === 'image',
      new StaleWhileRevalidate({
        cacheName: 'images-cache',
      })
    );

    Здесь нет inline-функций, что совместимо с строгой CSP.


CSP и манифесты precache

Workbox использует массив __WB_MANIFEST для предзагрузки ресурсов. Этот массив генерируется на этапе сборки и должен быть встроен в внешний файл воркера, а не в HTML. Если вставлять manifest inline, CSP с 'unsafe-inline' запретит выполнение кода. Поэтому правильный подход — использовать отдельный JS-файл, включающий manifest:

// sw.js
import {precacheAndRoute} from 'workbox-precaching';
precacheAndRoute(self.__WB_MANIFEST);

Стратегии обхода ограничений CSP

  1. Использование nonce Если inline-скрипты неизбежны, CSP позволяет применять nonce:

    script-src 'self' 'nonce-xyz123';

    Скрипт должен иметь атрибут <script nonce="xyz123">. Однако для сервис-воркеров этот метод редко применяется, так как воркер регистрируется через отдельный файл.

  2. Использование strict-dynamic Политика script-src 'strict-dynamic' позволяет загружать скрипты через доверенные источники, игнорируя остальные правила 'self' и CDN. Комбинируется с nonce.

  3. Бандлинг Workbox Интеграция через инструменты сборки (Webpack, Vite) позволяет генерировать единый JS-файл воркера, исключающий inline-код и eval. Это упрощает CSP и повышает безопасность.


Рекомендации по безопасной интеграции

  • Всегда использовать отдельный файл сервис-воркера.
  • Избегать inline скриптов и eval().
  • Разрешать загрузку только проверенных скриптов и ресурсов.
  • Интегрировать precache манифест через внешние файлы.
  • Проверять все runtime-кешированные ресурсы на соответствие CSP.

Если требуется, можно дополнительно рассмотреть конкретные кейсы конфигурации CSP для популярных сборщиков и Workbox-версий, с примерами production-ready setup, включая динамическое обновление сервис-воркера без нарушения политики безопасности.