XSS (Cross-Site Scripting) — это тип уязвимости веб-приложений, при котором злоумышленник внедряет вредоносный скрипт в контент, отображаемый другим пользователям. Основная цель XSS — выполнение кода в браузере жертвы с целью кражи сессий, перенаправления на сторонние ресурсы или изменения содержимого страницы.
Существует три основных типа XSS-атак:
При использовании Markdown-библиотек, таких как markdown-it, особое внимание уделяется обработке пользовательского ввода. Markdown позволяет вставлять HTML-теги и атрибуты, что открывает путь для внедрения скриптов через:
<script> теги;onload,
onclick);jav * ascript: схемой;Markdown-it по умолчанию не выполняет санитацию HTML, поэтому прямое отображение пользовательского ввода без фильтрации создаёт уязвимость.
const MarkdownIt = require('markdown-it');
const md = new MarkdownIt({
html: false, // Запрет на прямой HTML
xhtmlOut: false, // Генерация корректного XHTML
breaks: true, // Перевод переносов строк в <br>
linkify: true, // Автоматическое создание ссылок из URL
typographer: true // Поддержка типографики
});
Ключевые моменты:
html: false предотвращает интерпретацию
<script> и других HTML-тегов.linkify: true позволяет безопасно превращать URL в
ссылки без ручного HTML, снижая риск XSS через атрибуты
href.Markdown-it поддерживает плагины, которые помогают обезопасить вывод:
<b>,
<i>, <strong>,
<em>).jav * ascript:.Пример интеграции с DOMPurify:
const DOMPurify = require('dompurify')(window);
function renderMarkdown(input) {
const rawHtml = md.render(input);
return DOMPurify.sanitize(rawHtml);
}
Для более строгого контроля можно использовать маркер фильтрации тегов:
md.renderer.rules.html_block = () => '';
md.renderer.rules.html_inline = () => '';
Эта настройка полностью отключает рендеринг HTML, оставляя только Markdown-разметку безопасной.
Даже при отключенном HTML пользователи могут попытаться внедрить
jav * ascript: ссылки через Markdown-ссылки:
[Click me](jav * ascript:alert('XSS'))
Для защиты можно использовать проверку схем URL:
const sanitizeLink = (url) => {
const allowed = ['http:', 'https:', 'mailto:'];
try {
const parsed = new URL(url);
return allowed.includes(parsed.protocol) ? url : '#';
} catch {
return '#';
}
};
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
const hrefIndex = tokens[idx].attrIndex('href');
if (hrefIndex >= 0) {
tokens[idx].attrs[hrefIndex][1] = sanitizeLink(tokens[idx].attrs[hrefIndex][1]);
}
return self.renderToken(tokens, idx, options);
};
Дополнительно к фильтрации стоит применять CSP:
Content-Security-Policy: default-src 'self'; script-src 'none'; object-src 'none'
Эта политика блокирует выполнение встроенных скриптов и ограничивает загрузку внешних ресурсов.
Эти меры обеспечивают многоуровневую защиту, снижая вероятность успешной XSS-атаки при использовании markdown-it.