Безопасность пользовательского контента

MDX сочетает возможности Markdown и JSX, что позволяет встраивать компоненты React прямо в текст. Это даёт мощные возможности для динамических страниц, документации и блогов, однако открывает риски, связанные с безопасностью пользовательского контента. Основная угроза заключается в возможности внедрения вредоносного кода через JSX или HTML, встроенный в MDX.

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

Любой пользовательский MDX должен проходить этап валидации до рендеринга. Основные стратегии:

  1. Ограничение допустимых элементов Использовать специализированные парсеры, например remark с плагином remark-allowed-elements, чтобы разрешать только безопасный набор тегов. Например, можно разрешить p, h1-h6, ul, ol, li, strong, em, a, исключив любые <script>, <iframe> или произвольные компоненты JSX.

  2. Санитизация HTML Любой HTML, встроенный в MDX, должен проходить через санитайзер, такой как DOMPurify. Он удаляет опасные атрибуты (onload, onclick и подобные) и запрещённые теги.

  3. Ограничение JSX-компонентов Для MDX-файлов с поддержкой компонентов необходимо явно перечислять, какие компоненты допустимы. Пример:

    import { MDXProvider } from '@mdx-js/react';
    import SafeButton from './SafeButton';
    import SafeImage from './SafeImage';
    
    const components = {
      Button: SafeButton,
      Image: SafeImage
    };
    
    <MDXProvider components={components}>
      <MDXContent />
    </MDXProvider>

    Любой пользовательский JSX вне списка components должен быть запрещён.

Изоляция выполнения кода

MDX позволяет вставлять произвольный JavaScript через JSX, что создаёт риск выполнения вредоносного кода. Стратегии минимизации:

  • Серверная компиляция MDX-файлы обрабатываются на сервере, и конечный результат рендерится как статический HTML. Это исключает выполнение произвольного JS на клиенте.

  • Песочница для компонентов Использовать библиотеку вроде vm2 для безопасного выполнения кода, если динамические выражения неизбежны.

  • Ограничение импорта Не разрешать импорт внешних библиотек из пользовательского контента. Любые необходимые компоненты должны быть предоставлены через заранее подготовленный MDXProvider.

Контроль ссылок и ресурсов

MDX часто используется для публикации статей с ссылками и медиа-контентом. Для безопасности:

  • Добавлять атрибут rel="noopener noreferrer" ко всем внешним ссылкам.
  • Ограничивать доступ к локальным файлам и системным путям.
  • Проверять URL медиафайлов и изображений на предмет безопасных источников.

Пример безопасного рендеринга ссылок с использованием remark и rehype:

import rehypeExternalLinks from 'rehype-external-links';

const processor = unified()
  .use(remarkParse)
  .use(remarkRehype)
  .use(rehypeExternalLinks, { target: '_blank', rel: ['noopener', 'noreferrer'] })
  .use(rehypeStringify);

const safeHtml = processor.processSync(mdxContent).toString();

Лимиты и контроль ресурсов

Для защиты от атак через перегрузку:

  • Размер MDX-файла Ограничивать максимальный размер файла.
  • Глубина дерева JSX Ограничивать глубину вложенных компонентов, чтобы предотвратить рекурсивные вызовы.
  • Тайм-ауты рендеринга Для динамических выражений задавать лимиты времени выполнения.

Логирование и мониторинг

Даже при строгой фильтрации важно отслеживать попытки внедрения опасного контента. Следует вести:

  • Логирование всех ошибок при парсинге MDX.
  • Запись случаев отклонённого пользовательского кода.
  • Мониторинг аномального поведения при рендеринге страниц.

Резюме ключевых подходов

  • Санитизация HTML и контроль JSX-компонентов
  • Серверная компиляция и изоляция выполнения кода
  • Безопасные ссылки и медиа-ресурсы
  • Ограничения размера и глубины MDX-контента
  • Мониторинг и логирование опасных попыток

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