При работе с Markdown-it в JavaScript одной из ключевых задач является предотвращение потенциальных уязвимостей, связанных с XSS (Cross-Site Scripting) и внедрением вредоносного HTML-кода. Markdown-it по умолчанию интерпретирует разметку Markdown в HTML, что делает необходимым контролировать, какие элементы и атрибуты разрешены в итоговом выводе.
Ключевые угрозы:
<script>, onload, onclick и
другие атрибуты могут быть внедрены пользователем.<img src="jav * ascript:..."> или
<iframe> могут использоваться для выполнения
нежелательных действий.style могут быть использованы для обхода ограничений
отображения или подмены интерфейса.Markdown-it предоставляет базовый функционал рендеринга Markdown, но для безопасного использования в публичных веб-приложениях необходимо применять фильтры и плагины для санитизации.
Markdown-it поддерживает набор конфигурационных опций, которые помогают ограничить возможности ввода:
html – разрешает или запрещает
HTML-теги внутри Markdown.
const md = require('markdown-it')({ html: false });
Установка false полностью блокирует произвольный
HTML.
linkify – автоматическое
преобразование URL в ссылки. При включении необходимо дополнительно
проверять атрибуты target и rel для
предотвращения target="_blank" атак.
typographer – преобразует кавычки,
тире и т.д. Опасности XSS напрямую не создает, но важно понимать, как
рендерятся специальные символы.
Эти базовые опции создают первый уровень защиты, но для полноценной безопасности требуется использовать внешние средства санитизации.
Для безопасного отображения HTML-кода после рендеринга Markdown используется санитизация. Наиболее популярными библиотеками в экосистеме Node.js являются DOMPurify и sanitize-html.
Пример с DOMPurify:
const DOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const window = new JSDOM('').window;
const purify = DOMPurify(window);
const md = require('markdown-it')();
const unsafeHtml = md.render(userInput);
const safeHtml = purify.sanitize(unsafeHtml, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'ul', 'li', 'ol', 'code', 'pre'],
ALLOWED_ATTR: ['href', 'title']
});
Важные моменты:
style, class или
id могут быть потенциально опасны, особенно если
обрабатываются динамически.rel="noopener noreferrer".Markdown часто используется для генерации ссылок. Необходимо проверять URL, чтобы исключить:
jav * ascript: схемыdata: схемы для внедрения Base64-контентаfile: или
vb * script:Пример безопасной фильтрации ссылок:
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
const hrefIndex = tokens[idx].attrIndex('href');
if (hrefIndex >= 0) {
const url = tokens[idx].attrs[hrefIndex][1];
if (!url.startsWith('http') && !url.startsWith('https')) {
tokens[idx].attrs[hrefIndex][1] = '#';
}
tokens[idx].attrPush(['rel', 'noopener noreferrer']);
tokens[idx].attrPush(['target', '_blank']);
}
return self.renderToken(tokens, idx, options);
};
Markdown-it поддерживает подключение плагинов и определение кастомных правил рендеринга, что позволяет контролировать и фильтровать контент на уровне токенов:
src
перед рендерингом позволяет запрещать внешние ресурсы или ограничивать
размеры.Пример кастомного правила для блокировки
<iframe>:
md.core.ruler.push('remove_iframe', state => {
state.tokens = state.tokens.filter(token => {
if (token.type === 'html_block' && token.content.includes('<iframe')) {
return false;
}
return true;
});
});
html: false) при
отображении контента от пользователей.Эти практики обеспечивают безопасный вывод Markdown-контента и предотвращают большинство распространённых атак на веб-приложения.