Content Security Policy (CSP) — это механизм безопасности веб-приложений, позволяющий ограничивать источники загружаемых ресурсов и предотвращать выполнение вредоносного кода, такого как XSS-атаки. В контексте работы с библиотеками Remark и Rehype, CSP играет ключевую роль, особенно при рендеринге динамического контента, Markdown и HTML.
Remark используется для парсинга Markdown и трансформации его в промежуточное представление (AST — Abstract Syntax Tree). При преобразовании Markdown в HTML важно учитывать, что сгенерированный контент может содержать:
<script>)style атрибуты)Без строгой CSP существует риск выполнения непроверенного кода. Основные подходы к безопасному рендерингу:
Удаление потенциально опасных элементов
Используется плагин remark-sanitize или собственные
AST-трансформации для удаления или замены небезопасных узлов.
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkSanitize from 'remark-sanitize';
const processor = unified()
.use(remarkParse)
.use(remarkSanitize); // удаляет скрипты и небезопасные атрибутыРазделение контента и скриптов Любой
динамический контент лучше рендерить без возможности включения
<script> или inline JS. CSP можно настроить так,
чтобы разрешить только внешние скрипты с доверенных доменов.
Rehype берет AST, сгенерированный Remark, и превращает его в HTML. На этом этапе CSP может ограничивать использование:
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 и библиотеке работать совместно для надежного ограничения
внешнего контента.
Для обеспечения максимальной безопасности рекомендуется:
Запрет inline-скриптов и стилей
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self';Ограничение источников изображений и медиа
img-src 'self' https://trusted-cdn.com;
media-src 'self';Использование nonce для допустимых
inline-скриптов Если приложение генерирует динамические скрипты
(например, для подсветки кода), каждому скрипту назначается уникальный
nonce, и CSP позволяет выполнение только этих скриптов:
Content-Security-Policy: script-src 'nonce-ABC123';Политика подключения к внешним ресурсам Для безопасной загрузки шрифтов, API и фреймов:
connect-src 'self' https://api.example.com;
font-src 'self' https://fonts.example.com;
frame-src 'none';AST-трансформация вместо прямого HTML Прямой
вывод Markdown в HTML без фильтрации крайне рискован. Преобразование
через Remark → Rehype с использованием sanitize —
обязательная практика.
Ограничение 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);Валидация URL и ресурсов Даже при разрешении
<img> или <a> необходимо
проверять, что URL ведет только на доверенные домены.
helmet:import helmet from 'helmet';
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'"],
imgSrc: ["'self'", "https://trusted-cdn.com"]
}
})
);
Эта связка обеспечивает комплексную защиту от XSS, загрузки внешнего вредоносного контента и внедрения небезопасных скриптов, сохраняя при этом гибкость рендеринга Markdown и расширяемость через плагины Remark и Rehype.