Обработка небезопасного контента

Markdown-it предоставляет мощный движок для парсинга Markdown в JavaScript, однако при работе с пользовательским вводом возникает риск внедрения небезопасного контента, особенно HTML, JavaScript и URL. Для безопасной обработки необходимо использовать встроенные механизмы и сторонние плагины.


Встроенные опции безопасности

1. html

Опция html позволяет управлять поддержкой HTML-тегов внутри Markdown. По умолчанию она отключена:

const md = require('markdown-it')({
  html: false
});
  • html: true — разрешает интерпретацию HTML-тегов.
  • html: false — все HTML-теги экранируются и выводятся как текст.

Для предотвращения XSS (Cross-Site Scripting) рекомендуется держать html: false, если входные данные приходят от пользователей.


2. linkify

Опция linkify автоматически преобразует URL и email в ссылки:

const md = require('markdown-it')({
  linkify: true
});

Важно понимать, что автоматическое создание ссылок может быть источником уязвимостей через jav * ascript: URL. Для защиты следует использовать функцию-проверку URL:

const md = require('markdown-it')({
  linkify: true,
  linkTarget: '_blank'
});

const defaultRender = md.renderer.rules.link_open || function(tokens, idx, options, env, self) {
  return self.renderToken(tokens, idx, options);
};

md.renderer.rules.link_open = function (tokens, idx, options, env, self) {
  const hrefIndex = tokens[idx].attrIndex('href');
  if (hrefIndex >= 0) {
    const url = tokens[idx].attrs[hrefIndex][1];
    if (url.startsWith('jav * ascript:')) {
      tokens[idx].attrs[hrefIndex][1] = '#';
    }
  }
  return defaultRender(tokens, idx, options, env, self);
};

Сторонние библиотеки для фильтрации

1. DOMPurify

Для безопасного рендеринга HTML-тегов используется библиотека DOMPurify:

const DOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');

const window = (new JSDOM('')).window;
const purify = DOMPurify(window);

const unsafeHtml = md.render(userInput);
const safeHtml = purify.sanitize(unsafeHtml);
  • Фильтруются скрипты, iframe, inline event handlers.
  • Сохраняется базовая разметка HTML, если она разрешена.

2. sanitize-html

Альтернатива DOMPurify — sanitize-html, позволяющая более детально контролировать разрешённые теги и атрибуты:

const sanitizeHtml = require('sanitize-html');

const safeHtml = sanitizeHtml(md.render(userInput), {
  allowedTags: ['b', 'i', 'em', 'strong', 'a', 'p', 'ul', 'li', 'ol'],
  allowedAttributes: {
    'a': ['href', 'name', 'target']
  },
  allowedSchemes: ['http','https','mailto']
});
  • allowedSchemes предотвращает использование опасных схем вроде jav * ascript:.
  • allowedAttributes ограничивает атрибуты, которые могут быть внедрены.

Настройка безопасного рендеринга ссылок и изображений

Markdown-it позволяет кастомизировать рендеринг через renderer rules:

md.renderer.rules.image = function (tokens, idx, options, env, self) {
  const token = tokens[idx];
  const srcIndex = token.attrIndex('src');
  if (srcIndex >= 0) {
    const src = token.attrs[srcIndex][1];
    if (!src.startsWith('https://') && !src.startsWith('http://')) {
      token.attrs[srcIndex][1] = '';
    }
  }
  return self.renderToken(tokens, idx, options);
};
  • Блокирует небезопасные ссылки на изображения.
  • Можно комбинировать с DOMPurify для полной защиты.

Политика безопасного контента

При обработке Markdown от пользователей рекомендуется придерживаться многоуровневой стратегии безопасности:

  1. Отключение встроенного HTML (html: false).
  2. Фильтрация ссылок через кастомные правила рендеринга.
  3. Использование безопасной библиотеки (DOMPurify или sanitize-html) для финального HTML.
  4. Ограничение схем URL и атрибутов.
  5. Проверка изображений и медиа-файлов на безопасные источники.

Практический пример полной безопасной конфигурации

const MarkdownIt = require('markdown-it');
const DOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');

const window = (new JSDOM('')).window;
const purify = DOMPurify(window);

const md = new MarkdownIt({
  html: false,
  linkify: true
});

// Фильтрация ссылок
const defaultLinkRender = md.renderer.rules.link_open || function(tokens, idx, options, env, self) {
  return self.renderToken(tokens, idx, options);
};

md.renderer.rules.link_open = function(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://') && !url.startsWith('mailto:')) {
      tokens[idx].attrs[hrefIndex][1] = '#';
    }
  }
  return defaultLinkRender(tokens, idx, options, env, self);
};

// Рендер Markdown в безопасный HTML
function renderSafeMarkdown(input) {
  const rawHtml = md.render(input);
  return purify.sanitize(rawHtml);
}

Эта конфигурация гарантирует, что любой пользовательский Markdown будет преобразован в безопасный HTML без риска XSS и других атак через внедрение скриптов.