Проблемы XSS-атак

Понимание XSS-уязвимостей

XSS (Cross-Site Scripting) представляет собой класс уязвимостей веб-приложений, при котором злоумышленник может внедрять вредоносный JavaScript-код в страницы, отображаемые пользователям. В контексте использования библиотеки Marked, основной риск связан с рендерингом Markdown, который может содержать потенциально опасный HTML-код.

Любой Markdown-контент, получаемый из ненадёжного источника (например, пользовательский ввод или внешние API), может включать <script>-теги или встроенные обработчики событий (onclick, onmouseover и другие). Если Marked рендерит такой контент без фильтрации, это приведёт к выполнению вредоносного скрипта в браузере посетителя.

Встроенные механизмы защиты в Marked

Библиотека Marked предоставляет несколько способов контролировать безопасный рендеринг:

  1. Опция sanitize До версии 1.0.0 существовала встроенная опция sanitize: true, которая автоматически экранировала HTML-теги. В современных версиях Marked этот подход удалён, что делает необходимым использование сторонних средств фильтрации HTML.

  2. Опция mangle Опция отвечает за защиту email-адресов от спама. Она также влияет на обработку текста и может частично препятствовать внедрению некоторых видов HTML-инъекций.

  3. Опция breaks Позволяет управлять разрывами строк, но не влияет на XSS напрямую. Важно понимать, что Markdown-разрывы сами по себе безопасны.

Практические методы защиты от XSS

  1. Экранирование HTML-кода Вместо того чтобы полностью доверять Marked, необходимо предварительно экранировать все HTML-теги. Например, с использованием сторонней библиотеки вроде DOMPurify:

    import { marked } from 'marked';
    import DOMPurify from 'dompurify';
    
    const rawMarkdown = "<img src=x oner ror=alert('XSS')>";
    const html = marked(rawMarkdown);
    const safeHtml = DOMPurify.sanitize(html);

    Здесь DOMPurify удаляет все опасные теги и атрибуты, оставляя безопасный HTML.

  2. Отключение встроенного HTML в Marked Marked позволяет полностью игнорировать HTML, используя опцию gfm и специальный токенизатор:

    marked.setOptions({
      gfm: true,
      headerIds: false,
      mangle: false
    });
    
    const lexer = new marked.Lexer({ sanitize: false, walkTokens: (token) => {
      if (token.type === 'html') {
        token.type = 'text';
      }
    }});
    
    const html = marked.parser(lexer.lex(rawMarkdown));

    Такой подход предотвращает рендеринг любых HTML-тегов, оставляя только безопасный Markdown.

  3. Контроль атрибутов и ссылок Даже если HTML разрешён, необходимо фильтровать опасные атрибуты, такие как on* события, jav * ascript: в ссылках и data: в изображениях. В DOMPurify это настраивается через конфигурацию:

    const clean = DOMPurify.sanitize(html, {
      ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'ul', 'li', 'img'],
      ALLOWED_ATTR: ['href', 'src', 'alt', 'title'],
      FORBID_ATTR: ['onclick', 'onerror']
    });
  4. Регулярное обновление библиотек XSS-уязвимости могут скрываться в старых версиях Marked или зависимостей. Поддержание актуальной версии библиотеки снижает риск внедрения новых атак.

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

  • Изображения могут содержать onerror и использовать jav * ascript: в src. Их следует фильтровать и при необходимости заменять на безопасные альтернативы.
  • Ссылки с jav * ascript: протоколом способны инициировать выполнение скриптов. Их необходимо проверять перед вставкой в DOM.

Практическая стратегия интеграции

  1. Всегда считать Markdown из ненадёжных источников потенциально опасным.
  2. Рендерить Markdown через Marked с отключённым HTML либо с последующей фильтрацией через DOMPurify.
  3. Контролировать все внешние ссылки и изображения.
  4. При возможности использовать Content Security Policy (CSP) для предотвращения выполнения встроенных скриптов.

Эти меры позволяют безопасно использовать Marked в веб-приложениях, минимизируя риски XSS-атак.