XSS-атаки и защита

XSS (Cross-Site Scripting) — это тип уязвимости веб-приложений, при котором злоумышленник внедряет вредоносный скрипт в контент, отображаемый другим пользователям. Основная цель XSS — выполнение кода в браузере жертвы с целью кражи сессий, перенаправления на сторонние ресурсы или изменения содержимого страницы.

Существует три основных типа XSS-атак:

  • Reflected XSS — внедрение кода через параметры запроса, которые сервер возвращает без надлежащей фильтрации. Скрипт исполняется при открытии специально сформированного URL.
  • Stored XSS — скрипт сохраняется на сервере (например, в базе данных) и выполняется каждый раз при отображении заражённого контента пользователю.
  • DOM-based XSS — вредоносный код выполняется напрямую в браузере за счёт манипуляций с DOM, без участия сервера.

Риски XSS в контексте Markdown

При использовании Markdown-библиотек, таких как markdown-it, особое внимание уделяется обработке пользовательского ввода. Markdown позволяет вставлять HTML-теги и атрибуты, что открывает путь для внедрения скриптов через:

  • <script> теги;
  • Inline JavaScript в атрибутах (onload, onclick);
  • Ссылки с jav * ascript: схемой;
  • Встраивание внешних ресурсов с потенциально опасным содержимым.

Markdown-it по умолчанию не выполняет санитацию HTML, поэтому прямое отображение пользовательского ввода без фильтрации создаёт уязвимость.

Конфигурация markdown-it для безопасного рендеринга

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 поддерживает плагины, которые помогают обезопасить вывод:

  • markdown-it-sanitizer — фильтрует потенциально опасный HTML, оставляя безопасные теги (<b>, <i>, <strong>, <em>).
  • dompurify (в связке с markdown-it) — мощная библиотека для клиентской санитации, удаляет скрипты, обработчики событий и атрибуты типа jav * ascript:.

Пример интеграции с DOMPurify:

const DOMPurify = require('dompurify')(window);

function renderMarkdown(input) {
  const rawHtml = md.render(input);
  return DOMPurify.sanitize(rawHtml);
}

Ограничение встроенного HTML

Для более строгого контроля можно использовать маркер фильтрации тегов:

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);
};

Защита через Content Security Policy (CSP)

Дополнительно к фильтрации стоит применять CSP:

Content-Security-Policy: default-src 'self'; script-src 'none'; object-src 'none'

Эта политика блокирует выполнение встроенных скриптов и ограничивает загрузку внешних ресурсов.

Практика безопасного рендеринга Markdown

  1. Всегда отключать HTML, если он не нужен.
  2. Использовать DOMPurify или аналог для очистки вывода.
  3. Проверять URL и ссылки, блокируя потенциально опасные схемы.
  4. Ограничивать плагины, которые могут вставлять HTML или JS.
  5. Применять CSP на стороне сервера, чтобы минимизировать последствия ошибок фильтрации.

Эти меры обеспечивают многоуровневую защиту, снижая вероятность успешной XSS-атаки при использовании markdown-it.