FlawOpen

flawopen.com/XSS in JavaScript/DOMPurify and innerHTML

Достаточно ли DOMPurify для защиты innerHTML?

Справочник
Короткий ответ

DOMPurify — надёжный очиститель HTML по списку разрешений, если результат без изменений сразу передаётся HTML-sink. Представьте проверку посылки на входе: если её потом перепакуют или внесут через другой вход, первая проверка уже не описывает содержимое. Для обычного текста используйте textContent, чтобы вообще не разбирать HTML.

РИСК СОХРАНЯЕТСЯ — старый результат попадает в sink
// Очищено при сохранении старой версией/политикой.
saveToDb(DOMPurify.sanitize(input));

// Позже сохранённой строке доверяют без проверки.
el.innerHTML = fetchedFromDb;
БЕЗОПАСНЕЕ — очистка для HTML-sink
// Для HTML используйте актуальную библиотеку
// прямо перед вставкой в HTML-контекст.
el.innerHTML = DOMPurify.sanitize(untrustedHtml, {
  USE_PROFILES: { html: true },
});

// Обычный текст не нужно разбирать как HTML.
textNode.textContent = untrustedText;

Почему очистка только при сохранении слабее

Очистка при сохранении сама по себе не опасна, но создаёт границу устаревших данных: renderer может доверять старому результату после исправления очистителя, смены политики или обходного пути. Повторно очищайте данные при выводе актуальной версией и политикой точного sink. Не меняйте очищенную разметку и не передавайте её другой библиотеке до вставки: постобработка может отменить результат очистки.

Важны конфигурация и контекст вывода

DOMPurify по умолчанию разрешает HTML, SVG и MathML. Если нужен только HTML, сузьте профиль через USE_PROFILES: { html: true }. Не расширяйте список тегов и атрибутов без проверки. DOMPurify не очищает CSS и не блокирует все внешние запросы; используйте результат только в HTML-контексте, не в JavaScript, атрибуте, XML, SVG или CSS.

Trusted Types и серверный рендеринг

Если поддерживается, включите Trusted Types через Content Security Policy, чтобы отклонять строки в HTML-sink без одобренной политики. DOMPurify может возвращать TrustedHTML; это предотвращает случайные присваивания в sink, но не исправляет плохую конфигурацию или постобработку. На сервере DOMPurify зависит от DOM-реализации: обновляйте jsdom и не используйте с ним happy-dom для критичной к безопасности очистки.

Производственный чек-лист

Поиск HTML-sink в коде

rg -n -g '*.{js,ts,jsx,tsx}' '(innerHTML|outerHTML|insertAdjacentHTML|document\.write|dangerouslySetInnerHTML)' .
Для каждого совпадения проследите недоверенные данные до sink, подтвердите очистку прямо перед вставкой и проверьте, что последующий код не меняет разметку.

Источники и ссылки

См. также XSS (межсайтовый скриптинг, CWE-79): как он работает и как его предотвратить в любом языке — flawopen.com Защищает ли Markdown от XSS? Защищает ли Svelte от XSS по умолчанию? Опасна ли загрузка SVG из-за XSS? Риск XSS при использовании Vue v-html WKWebView: безопасен ли HTML? Отражённый, хранимый и DOM-based XSS: в чём разница?