Библиотека Markdown-it предоставляет мощный парсер Markdown в JavaScript, превращая текст с разметкой в валидный HTML. Однако при отображении пользовательского контента важно учитывать безопасность, чтобы предотвратить XSS-атаки. Для этого используется DOMPurify — инструмент для очистки HTML от вредоносного кода.
Классический сценарий работы выглядит следующим образом: Markdown-текст обрабатывается через Markdown-it, результат конвертируется в HTML, после чего проходит через DOMPurify для фильтрации небезопасных элементов и атрибутов.
import MarkdownIt from 'markdown-it';
import DOMPurify from 'dompurify';
const md = new MarkdownIt();
const rawMarkdown = `# Пример заголовка
<script>alert('XSS')</script>
**Жирный текст**`;
const html = md.render(rawMarkdown);
const safeHtml = DOMPurify.sanitize(html);
document.getElementById('content').innerHTML = safeHtml;
В этом примере <script> тег автоматически
удаляется DOMPurify, а Markdown-разметка корректно преобразуется в
HTML.
DOMPurify предоставляет широкие возможности настройки, включая:
https:).Пример ограничения атрибутов:
const cleanHtml = DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'ul', 'li', 'h1', 'h2', 'h3'],
ALLOWED_ATTR: ['href', 'title']
});
Это особенно важно при интеграции Markdown с пользовательскими ссылками, чтобы не допустить открытие вредоносных URL.
Markdown-it поддерживает систему плагинов, которая расширяет синтаксис и добавляет новые возможности, такие как таблицы, чекбоксы, математические формулы и многое другое. После применения плагина результат также необходимо фильтровать через DOMPurify.
Пример с плагином таблиц:
import markdownItTable from 'markdown-it-multimd-table';
const md = new MarkdownIt().use(markdownItTable);
const tableMarkdown = `
| Имя | Возраст |
|------|---------|
| Иван | 25 |
`;
const htmlTable = md.render(tableMarkdown);
const safeHtmlTable = DOMPurify.sanitize(htmlTable);
Даже если плагин генерирует сложные HTML-структуры, DOMPurify гарантирует удаление нежелательных тегов или атрибутов.
При рендеринге больших объёмов Markdown важно учитывать эффективность фильтрации. DOMPurify достаточно быстрый, но обработка тысяч элементов может быть ресурсоёмкой. Рекомендуется:
let cache = new Map();
function renderMarkdownSafe(markdown) {
if (cache.has(markdown)) return cache.get(markdown);
const html = md.render(markdown);
const safe = DOMPurify.sanitize(html);
cache.set(markdown, safe);
return safe;
}
DOMPurify позволяет использовать хуки до и после очистки, что даёт возможность изменять HTML или добавлять собственную валидацию. Основные хуки:
beforeSanitizeElements — вызывается перед обработкой
тегов.uponSanitizeElement — вызывается при проверке каждого
элемента.uponSanitizeAttribute — вызывается для каждого атрибута
элемента.afterSanitizeElements — вызывается после очистки
элементов.Пример удаления всех <img> тегов с небезопасными
src:
DOMPurify.addHook('uponSanitizeAttribute', (node, data) => {
if (data.attrName === 'src' && node.tagName === 'IMG') {
if (!/^https?:\/\//.test(data.attrValue)) {
data.keepAttr = false;
}
}
});
Это обеспечивает гибкость при работе с Markdown, особенно когда пользователь может вставлять HTML или использовать расширенные плагины Markdown-it.
При работе с фреймворками вроде React, Vue или Svelte необходимо учитывать особенности рендеринга HTML:
dangerouslySetInnerHTML, но
после очистки через DOMPurify.v-html.{@html ...} для вставки HTML.Пример с React:
function MarkdownRenderer({ content }) {
const html = md.render(content);
const safeHtml = DOMPurify.sanitize(html);
return <div dangerouslySetInnerHTML={{ __html: safeHtml }} />;
}
Это предотвращает XSS даже при динамическом контенте, генерируемом пользователями.
Markdown-it в связке с DOMPurify обеспечивает безопасное и гибкое отображение пользовательского Markdown, позволяя сохранять богатую разметку, таблицы, ссылки и другие расширенные элементы, одновременно исключая возможность внедрения вредоносного кода.