Content Security Policy

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


Базовый CSP заголовок

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

При такой настройке timeago.js, загруженный из CDN стороннего сервера, будет заблокирован.


CSP для timeago.js из CDN

Если библиотека загружается с CDN:

<script src="https://cdn.jsdelivr.net/npm/timeago.js/dist/timeago.min.js"
        integrity="sha384-..."
        crossorigin="anonymous"></script>
Content-Security-Policy: script-src 'self' https://cdn.jsdelivr.net

Для дополнительной защиты использовать integrity (Subresource Integrity):

<script src="https://cdn.jsdelivr.net/npm/timeago.js@4.0.2/dist/timeago.min.js"
        integrity="sha384-rr8NMDKlGDgJz6/3CdWXGHC5Y5Q6pQfQUfHfQnD3hS6Y2Lv9l1J6KBfQb1yMag="
        crossorigin="anonymous"></script>

CSP для бандлера (webpack, vite)

Когда timeago.js включён в собственный бандл, он загружается с того же домена:

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

Все скрипты должны поступать с 'self' — дополнительных разрешений не нужно.


CSP и inline скрипты

timeago.js не использует inline скрипты, поэтому unsafe-inline не требуется для самой библиотеки. Если в проекте нет других inline скриптов, CSP может быть строгим:

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

Nonce-based CSP (строгая политика)

Content-Security-Policy: script-src 'nonce-{RANDOM_NONCE}'

Только скрипты с атрибутом nonce будут выполнены:

<script nonce="abc123">
  // Этот скрипт будет выполнен
  import { render } from 'timeago.js';
  render(document.querySelector('time'), 'ru');
</script>
<script>
  // Этот скрипт будет заблокирован
</script>

Hash-based CSP

Если инлайн-скрипт статичен, можно использовать hash:

Content-Security-Policy: script-src 'sha256-{HASH_OF_SCRIPT}'
# Вычислить хеш скрипта
echo -n "import { render } from 'timeago.js';" | openssl dgst -sha256 -binary | base64

CSP для eval (важно для некоторых сборщиков)

Webpack в development-режиме может использовать eval:

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

В production это недопустимо. Настроить webpack:

// webpack.config.js
module.exports = {
  devtool: process.env.NODE_ENV === 'production' ? false : 'source-map',
};

source-map не требует unsafe-eval в отличие от eval-source-map.


CSP для Vite

// vite.config.ts
export default {
  plugins: [{
    name: 'csp-headers',
    configureServer(server) {
      server.middlewares.use((req, res, next) => {
        res.setHeader('Content-Security-Policy',
          "default-src 'self'; script-src 'self'"
        );
        next();
      });
    },
  }],
};

Проверка CSP в браузере

  1. Открыть DevTools → Console.

  2. При нарушении CSP появится сообщение:

    Refused to load the script 'https://...' because it violates the following Content Security Policy directive: ...
  3. Network → Headers → Content-Security-Policy — посмотреть текущую политику.


Заголовок Report-URI для мониторинга нарушений

Content-Security-Policy: script-src 'self'; report-uri /csp-report
// Express — обработчик отчётов
app.post('/csp-report', (req, res) => {
  console.log('CSP violation:', req.body);
  res.status(204).end();
});

Полный пример строгого CSP

Content-Security-Policy:
  default-src 'none';
  script-src 'self';
  style-src 'self';
  img-src 'self' dat a:;
  font-src 'self';
  connect-src 'self';
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self'

При такой политике timeago.js должен быть включён в собственный бандл (script-src 'self'), а CDN загрузка запрещена.


Чеклист CSP для проектов с timeago.js

Условие Настройка CSP
timeago.js из собственного бандла script-src 'self'
timeago.js с CDN script-src 'self' https://cdn.jsdelivr.net + SRI
Нет inline скриптов Не нужен unsafe-inline
Development webpack unsafe-eval только для dev
Мониторинг нарушений report-uri /csp-report