Санитизация HTML

Библиотека Marked предназначена для парсинга Markdown в HTML и широко используется в веб-приложениях. Одной из ключевых проблем при работе с пользовательским вводом является безопасность HTML, поскольку некорректная обработка может привести к XSS-уязвимостям. Санитизация HTML позволяет контролировать, какие теги и атрибуты разрешены в выходном HTML.


1. Включение и отключение встроенного HTML

По умолчанию Marked поддерживает встроенный HTML в Markdown. Это означает, что конструкции вроде текст будут корректно преобразованы и отображены как HTML.

Для контроля этой функции используется опция:

marked.setOptions({
  gfm: true,        // поддержка GitHub Flavored Markdown
  breaks: false,    // переносы строк
  sanitize: false,  // устаревшая опция
  headerIds: true
});

Внимание: опция sanitize устарела начиная с Marked v1.0 и больше не используется для полной безопасности. Для надежной защиты требуется внешняя библиотека санитизации.


2. Безопасная санитизация через DOMPurify

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

import DOMPurify from 'dompurify';
import { marked } from 'marked';

const markdown = `# Пример\nопасный HTML`;
const html = marked(markdown);
const cleanHTML = DOMPurify.sanitize(html, {
  ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'ul', 'ol', 'li', 'code', 'pre'],
  ALLOWED_ATTR: ['href', 'title']
});

В этом примере:

  • ALLOWED_TAGS ограничивает список HTML-тегов, которые будут разрешены в выходном HTML.
  • ALLOWED_ATTR контролирует допустимые атрибуты, предотвращая внедрение скриптов через on* атрибуты или jav * ascript: ссылки.

Использование DOMPurify гарантирует, что даже если Markdown содержит , этот код будет удален при рендеринге.


3. Санитизация на сервере и на клиенте

На сервере (Node.js):

import { marked } from 'marked';
import DOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';

const window = new JSDOM('').window;
const DOMPurifyServer = DOMPurify(window);

const markdown = 'Пример ';
const html = marked(markdown);
const safeHTML = DOMPurifyServer.sanitize(html);

Особенности:

  • Создание виртуального DOM через jsdom позволяет использовать DOMPurify на сервере.
  • Любые потенциально опасные теги или атрибуты удаляются до передачи HTML клиенту.

На клиенте (браузер):

const html = marked(userInputMarkdown);
const cleanHTML = DOMPurify.sanitize(html);
document.getElementById('content').innerHTML = cleanHTML;

4. Кастомизация рендерера для дополнительного контроля

Marked позволяет создавать пользовательский рендерер, чтобы контролировать вывод HTML:

const renderer = new marked.Renderer();

renderer.link = (href, title, text) => {
  const safeHref = DOMPurify.sanitize(href, { ALLOWED_URI_REGEXP: /^(https?:)?\/\// });
  return `${text}`;
};

marked.setOptions({ renderer });

Такой подход:

  • Предотвращает внедрение опасных ссылок через jav * ascript: или data: схемы.
  • Позволяет фильтровать контент на уровне конкретных элементов, не обрабатывая весь HTML глобально.

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

  1. Не использовать устаревшую опцию sanitize. Она не обеспечивает безопасную обработку HTML.
  2. Комбинировать Marked с проверенной библиотекой санитизации, например DOMPurify, для защиты от XSS.
  3. Ограничивать список тегов и атрибутов, особенно для пользовательского контента.
  4. Использовать кастомные рендереры, если нужно тонкое управление выводом элементов Markdown.
  5. Санитизировать HTML как на сервере, так и на клиенте, чтобы предотвратить обход фильтров.

6. Особенности обработки HTML-тегов

  • Блоковые элементы (
    ,

    ,

    ) могут быть разрешены
    или запрещены в зависимости от политики безопасности.
  • Инлайновые элементы (, , , ) обычно безопасны и разрешены.
  • Скриптовые и потенциально опасные элементы (