Инъекции через ссылки

Markdown-it — это современная библиотека для парсинга Markdown в JavaScript, обладающая высокой настраиваемостью через плагины и правила рендеринга. Один из аспектов, который требует особого внимания при работе с Markdown-it, — инъекции через ссылки, поскольку Markdown позволяет создавать гиперссылки с произвольным содержимым и атрибутами, что потенциально может стать вектором XSS-атак при неправильной обработке.

Механизм обработки ссылок в Markdown-it

Markdown-it парсит ссылки с использованием правил link_open и link_close. Любая ссылка в Markdown, например:

[текст ссылки](https://example.com)

разбивается на несколько токенов:

  1. link_open — токен, содержащий атрибут href.
  2. inline — внутренний токен с текстом ссылки.
  3. link_close — закрывающий токен.

При этом библиотека по умолчанию не фильтрует содержимое атрибута href, оставляя это на усмотрение разработчика, что открывает возможность для внедрения вредоносного JavaScript кода через ссылки вида:

[нажми меня](jav * ascript:alert('XSS'))

Потенциальные уязвимости

Основные сценарии, где возможны инъекции через ссылки:

  • JavaScript URI: ссылки, начинающиеся с jav * ascript: или data:text/html.
  • Фрагменты с небезопасными символами: onclick, onmouseover и другие атрибуты, если они добавлены через плагины или кастомные правила.
  • HTML-инъекции внутри текста ссылки, если разрешён inline HTML (html: true в конфиге).

Markdown-it при стандартной конфигурации позволяет безопасно рендерить Markdown, но при использовании html: true или кастомных правил рендеринга разработчик берёт на себя ответственность за безопасность.

Фильтрация ссылок

Для предотвращения инъекций важно фильтровать и проверять атрибут href. Например, можно использовать встроенную функцию для проверки протоколов:

const md = require('markdown-it')();

md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
  const hrefIndex = tokens[idx].attrIndex('href');
  if (hrefIndex >= 0) {
    const href = tokens[idx].attrs[hrefIndex][1];
    if (!/^https?:\/\//i.test(href)) {
      tokens[idx].attrs[hrefIndex][1] = '#';
    }
  }
  return self.renderToken(tokens, idx, options);
};

В этом примере все ссылки, не начинающиеся с http:// или https://, заменяются на безопасную заглушку #, предотвращая выполнение JavaScript через ссылку.

Использование плагинов для безопасности

Markdown-it имеет систему плагинов, которые позволяют безопасно обрабатывать ссылки:

  • markdown-it-link-attributes — добавляет безопасные атрибуты rel="nofollow noopener noreferrer" к ссылкам.
  • markdown-it-sanitizer — полностью фильтрует опасные HTML-теги и атрибуты.

Пример использования markdown-it-link-attributes:

const md = require('markdown-it')();
const linkAttributes = require('markdown-it-link-attributes');

md.use(linkAttributes, {
  pattern: /^https?:\/\//,
  attrs: {
    rel: 'nofollow noopener noreferrer',
    target: '_blank'
  }
});

Эта настройка гарантирует, что все внешние ссылки будут безопасно открываться в новом окне и не будут передавать управление JavaScript на страницу.

Обработка inline HTML

Markdown-it позволяет включать HTML напрямую через опцию html: true. Если эта опция включена, необходимо дополнительно санитизировать HTML:

const DOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const window = new JSDOM('').window;
const purify = DOMPurify(window);

const rawHtml = '<a href="jav * ascript:alert(1)">Click me</a>';
const safeHtml = purify.sanitize(rawHtml);

Такой подход предотвращает XSS, даже если Markdown содержит опасные ссылки или HTML-теги.

Рекомендации по безопасной работе со ссылками

  • Всегда проверять протокол в атрибуте href.
  • Использовать плагины для автоматического добавления безопасных атрибутов.
  • Санитизировать HTML при включении опции html: true.
  • Избегать использования пользовательского Markdown для генерации прямого JavaScript или данных в href.
  • Использовать тестирование на потенциальные XSS-уязвимости с реальными payload’ами.

Заключение по инъекциям через ссылки

Инъекции через ссылки в Markdown-it возникают в основном из-за небезопасных протоколов или разрешённого HTML. Системная фильтрация, плагины для безопасности и санитизация HTML позволяют безопасно работать с Markdown и минимизировать риск XSS. Настройка рендерера ссылок через правила link_open — основной инструмент для контроля безопасности.