Content Security Policy

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


CSP и рендеринг Markdown с Remark

Remark используется для парсинга Markdown и трансформации его в промежуточное представление (AST — Abstract Syntax Tree). При преобразовании Markdown в HTML важно учитывать, что сгенерированный контент может содержать:

  • Скрипты (<script>)
  • Inline-стили (style атрибуты)
  • HTML-теги, вставленные пользователями

Без строгой CSP существует риск выполнения непроверенного кода. Основные подходы к безопасному рендерингу:

  1. Удаление потенциально опасных элементов Используется плагин remark-sanitize или собственные AST-трансформации для удаления или замены небезопасных узлов.

    import { unified } from 'unified';
    import remarkParse from 'remark-parse';
    import remarkSanitize from 'remark-sanitize';
    
    const processor = unified()
      .use(remarkParse)
      .use(remarkSanitize); // удаляет скрипты и небезопасные атрибуты
  2. Разделение контента и скриптов Любой динамический контент лучше рендерить без возможности включения <script> или inline JS. CSP можно настроить так, чтобы разрешить только внешние скрипты с доверенных доменов.


CSP и преобразование AST в HTML с Rehype

Rehype берет AST, сгенерированный Remark, и превращает его в HTML. На этом этапе CSP может ограничивать использование:

  • Inline-стилей и inline-скриптов
  • Внешних ресурсов (изображения, шрифты, скрипты)
  • Фреймов и подключаемого контента (iframe, object)

Пример безопасного конвейера с использованием Rehype:

import rehypeSanitize from 'rehype-sanitize';
import rehypeStringify from 'rehype-stringify';
import { unified } from 'unified';

const htmlProcessor = unified()
  .use(rehypeSanitize) // базовая фильтрация HTML
  .use(rehypeStringify);

const safeHtml = await htmlProcessor.process(someAst);

rehype-sanitize поддерживает настройку схемы, где можно разрешить только безопасные теги, атрибуты и URL-источники. Это позволяет CSP и библиотеке работать совместно для надежного ограничения внешнего контента.


Настройка CSP для приложений с Remark/Rehype

Для обеспечения максимальной безопасности рекомендуется:

  1. Запрет inline-скриптов и стилей

    Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self';
  2. Ограничение источников изображений и медиа

    img-src 'self' https://trusted-cdn.com;
    media-src 'self';
  3. Использование nonce для допустимых inline-скриптов Если приложение генерирует динамические скрипты (например, для подсветки кода), каждому скрипту назначается уникальный nonce, и CSP позволяет выполнение только этих скриптов:

    Content-Security-Policy: script-src 'nonce-ABC123';
  4. Политика подключения к внешним ресурсам Для безопасной загрузки шрифтов, API и фреймов:

    connect-src 'self' https://api.example.com;
    font-src 'self' https://fonts.example.com;
    frame-src 'none';

Практика безопасной работы с Markdown

  1. AST-трансформация вместо прямого HTML Прямой вывод Markdown в HTML без фильтрации крайне рискован. Преобразование через Remark → Rehype с использованием sanitize — обязательная практика.

  2. Ограничение HTML-тегов В rehype-sanitize можно настроить список допустимых тегов:

    import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
    
    const schema = {
      ...defaultSchema,
      tagNames: ['p', 'strong', 'em', 'ul', 'ol', 'li', 'a', 'img'],
      attributes: {
        a: ['href', 'title'],
        img: ['src', 'alt']
      }
    };
    
    unified()
      .use(rehypeSanitize, schema);
  3. Валидация URL и ресурсов Даже при разрешении <img> или <a> необходимо проверять, что URL ведет только на доверенные домены.


Интеграция CSP и серверной обработки

  • На сервере Node.js CSP заголовки добавляются через middleware, например, helmet:
import helmet from 'helmet';
app.use(
  helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'"],
      styleSrc: ["'self'"],
      imgSrc: ["'self'", "https://trusted-cdn.com"]
    }
  })
);
  • Совместная работа с Remark/Rehype минимизирует риски: Remark фильтрует небезопасные Markdown-сценарии, Rehype очищает HTML, а CSP контролирует поведение браузера.

Эта связка обеспечивает комплексную защиту от XSS, загрузки внешнего вредоносного контента и внедрения небезопасных скриптов, сохраняя при этом гибкость рендеринга Markdown и расширяемость через плагины Remark и Rehype.