Перейти к основному содержимому

XSS и рендеринг данных

XSS-атаки позволяют выполнить произвольный JS-код, что ведет к краже токенов, перехвату сессий и подмене контента. Современные фреймворки экранируют данные по умолчанию. Этот защитный механизм обходить без крайней необходимости FORBIDDEN.

Правила

FORBIDDEN

FORBIDDEN: Использование механизмов вставки сырого HTML (dangerouslySetInnerHTML в React) без предварительной санитизации

FORBIDDEN: Присвоение недоверенных данных в innerHTML через ref без санитизации

FORBIDDEN: Динамическое формирование HTML-строк с включением пользовательских данных и их последующая вставка в DOM

FORBIDDEN: Самостоятельная реализация санитизации

FORBIDDEN: Использование Regular Expressions для очистки HTML

MUST

MUST: Если рендеринг HTML необходим (CMS, WYSIWYG, Markdown), санитизация выполняется непосредственно перед рендерингом

MUST: Использовать только проверенные библиотеки санитизации:

  • dompurify для клиентского рендеринга
  • isomorphic-dompurify для SSR-приложений

MUST: Применять принцип whitelist (явное указание разрешенных тегов и атрибутов)

Примеры

Запрещенные практики

// ❌ FORBIDDEN: Вставка сырого HTML без санитизации
function CommentBody({ html }: { html: string }) {
return <div dangerouslySetInnerHTML={{ __html: html }} />;
}

// ❌ FORBIDDEN: Использование innerHTML через ref
function CommentBody({ html }: { html: string }) {
const ref = useRef<HTMLDivElement>(null);

useEffect(() => {
if (ref.current) {
ref.current.innerHTML = html; // Опасно!
}
}, [html]);

return <div ref={ref} />;
}

// ❌ FORBIDDEN: Динамическое формирование HTML с пользовательскими данными
function UserGreeting({ name }: { name: string }) {
const html = `<h1>Hello, ${name}!</h1>`; // Опасно!
return <div dangerouslySetInnerHTML={{ __html: html }} />;
}

// ❌ FORBIDDEN: Самостоятельная санитизация через regex
function sanitizeHTML(html: string) {
return html
.replace(/<script/gi, '') // Недостаточно!
.replace(/on\w+=/gi, ''); // Обходится легко!
}

Правильный подход

// ✅ MUST: Санитизация с dompurify
import DOMPurify from 'dompurify';

function CommentBody({ html }: { html: string }) {
const sanitizedHtml = DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'a'],
ALLOWED_ATTR: ['href']
});

return <div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} />;
}

// ✅ MUST: Markdown с санитизацией (react-markdown)
import ReactMarkdown from 'react-markdown';

function CommentBody({ markdown }: { markdown: string }) {
return (
<ReactMarkdown
allowedElements={['p', 'br', 'strong', 'em', 'a', 'code']}
>
{markdown}
</ReactMarkdown>
);
}

// ✅ Лучше всего: Использование React без обхода экранирования
function UserGreeting({ name }: { name: string }) {
return <h1>Hello, {name}!</h1>; // React автоматически экранирует
}

Санитизация для SSR

// ✅ MUST: isomorphic-dompurify для SSR
import DOMPurify from 'isomorphic-dompurify';

export function ArticleContent({ html }: { html: string }) {
const sanitized = DOMPurify.sanitize(html, {
ALLOWED_TAGS: [
'h1', 'h2', 'h3', 'p', 'br', 'strong', 'em', 'ul', 'ol', 'li', 'a', 'code', 'pre'
],
ALLOWED_ATTR: ['href', 'target', 'rel']
});

return <article dangerouslySetInnerHTML={{ __html: sanitized }} />;
}

Whitelist конфигурация

// Пример whitelist для разных типов контента

// Базовый текст (комментарии)
const BASIC_TEXT_CONFIG = {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em'],
ALLOWED_ATTR: []
};

// Форматированный текст (статьи)
const RICH_TEXT_CONFIG = {
ALLOWED_TAGS: [
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'p', 'br', 'strong', 'em', 'u', 's',
'ul', 'ol', 'li',
'a', 'img',
'blockquote', 'code', 'pre'
],
ALLOWED_ATTR: ['href', 'src', 'alt', 'title', 'target', 'rel']
};

// Использование
function RichTextContent({ html }: { html: string }) {
const sanitized = DOMPurify.sanitize(html, RICH_TEXT_CONFIG);
return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
}

📌 Ключевые моменты:

  • Современные фреймворки экранируют данные по умолчанию
  • Обход экранирования FORBIDDEN без санитизации
  • MUST использовать whitelist подход
  • MUST использовать проверенные библиотеки (dompurify)
  • Санитизация выполняется непосредственно перед рендерингом