XSS атаки и их предотвращение

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

Существует несколько типов XSS:

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

Проблемы XSS в экосистеме Markdown

В веб-приложениях, где используется Markdown для ввода контента пользователями, XSS-риски увеличиваются. Markdown часто рендерится в HTML, и любой небезопасный HTML или атрибуты могут стать каналом для выполнения скриптов. Например, вставка <img src=x oner ror=alert(1)> в Markdown приведёт к выполнению JavaScript при рендере, если преобразование в HTML не фильтрует опасные теги.

Роль Remark и Rehype

Remark — это библиотека для разбора Markdown в AST (Abstract Syntax Tree) и обратного преобразования. Она предоставляет мощный механизм для обработки, трансформации и валидации Markdown-контента. Rehype выполняет аналогичную роль для HTML: преобразует HTML в AST и позволяет безопасно манипулировать узлами, включая фильтрацию потенциально опасных элементов.

Remark и Rehype используются совместно через плагины, создавая цепочку преобразования:

  1. Markdown → AST (Remark)
  2. AST Markdown → AST HTML (Remark-Rehype)
  3. AST HTML → HTML (Rehype)

Предотвращение XSS с помощью Remark и Rehype

1. Удаление опасных тегов и атрибутов

Rehype предоставляет плагины, которые фильтруют небезопасные теги и атрибуты. Наиболее распространённый подход — использовать rehype-sanitize. Он позволяет определить белый список тегов и атрибутов, исключая любые скриптовые конструкции:

import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeSanitize from 'rehype-sanitize';
import rehypeStringify from 'rehype-stringify';

const processor = unified()
  .use(remarkParse)
  .use(remarkRehype)
  .use(rehypeSanitize) // базовая безопасная фильтрация
  .use(rehypeStringify);

const html = await processor.process(someMarkdown);

rehype-sanitize может быть настроен с собственным белым списком тегов и атрибутов, что обеспечивает строгий контроль над безопасным HTML.

2. Очистка пользовательских ссылок

Внешние ссылки могут быть использованы для XSS через jav * ascript: URI или фишинговые перенаправления. rehype-sanitize поддерживает настройку protocols для разрешённых схем, например только http, https, mailto.

import { defaultSchema } from 'hast-util-sanitize';

const schema = {
  ...defaultSchema,
  attributes: {
    ...defaultSchema.attributes,
    a: {
      ...defaultSchema.attributes.a,
      href: ['http', 'https', 'mailto'], // разрешённые протоколы
    },
  },
};

3. Ограничение внедрения HTML в Markdown

Remark позволяет отключить или ограничить встроенный HTML с помощью плагинов вроде remark-gfm или remark-html. Если HTML нужен, его следует обрабатывать через Rehype с последующей санитацией, иначе лучше полностью запретить его:

import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeSanitize from 'rehype-sanitize';

// Не используем встроенный HTML в Markdown

4. Настройка белого списка для безопасного контента

Для проектов с расширенными требованиями безопасности рекомендуется создавать кастомные схемы для Rehype:

  • Разрешённые теги: p, strong, em, ul, li, a, code, pre, blockquote
  • Разрешённые атрибуты: href для ссылок, className для стилизации
  • Запрет любых inline-скриптов (on*) и style с JavaScript
import rehypeSanitize from 'rehype-sanitize';

const customSchema = {
  tagNames: ['p', 'strong', 'em', 'ul', 'li', 'a', 'code', 'pre', 'blockquote'],
  attributes: {
    a: ['href'],
    code: ['className'],
  },
  protocols: {
    href: ['http', 'https', 'mailto'],
  },
};

5. Использование цепочек плагинов

Эффективная защита XSS строится на комбинации плагинов Remark и Rehype:

  1. Remark-parse — преобразует Markdown в AST.
  2. Remark-rehype — конвертирует Markdown AST в HTML AST.
  3. Rehype-sanitize — фильтрует опасные теги и атрибуты.
  4. Rehype-stringify — преобразует безопасный AST в HTML.

Такая цепочка обеспечивает чистый и безопасный HTML-код, минимизируя риск XSS.

Рекомендации по безопасности

  • Не доверять пользовательскому контенту: всегда обрабатывать Markdown через проверенные плагины.
  • Минимизировать разрешённые HTML-теги: чем меньше тегов и атрибутов разрешено, тем меньше поверхность атаки.
  • Проверять ссылки и протоколы: исключать jav * ascript:, data:, vb * script:.
  • Регулярное обновление библиотек: новые версии Remark и Rehype устраняют известные уязвимости.
  • Логирование и мониторинг: отслеживание подозрительных вставок скриптов позволяет оперативно реагировать на новые XSS-методы.

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