Ловушки Vibe Coding: Топ-6 скрытых уязвимостей в React
Эта статья рассматривает проекты, созданные с использованием React + Next.js App Router. Принципы безопасности универсальны, но примеры кода и конфигурации (
next.config.js,vercel.json) специфичны для этого стека.
Вы попросили ИИ-ассистента написать компонент. Он его написал. Он работает. Тесты «зеленые». Вы нажимаете Merge.
А затем происходит утечка данных, вы получаете нулевой результат в Google PageSpeed, жалобы от пользователей с нарушениями зрения и вопрос: «Почему у нас не настроены заголовки безопасности?».
ИИ-инструменты для кодинга не знают контекста вашего продакшена. Для них нет разницы между учебным пособием для новичков и финансовым приложением. Результат — они пишут код, который «просто работает», но при этом незаметно нарушает правила из OWASP Top 10.
Вот 6 уязвимостей, которые ИИ-ассистенты незаметно вплетают в код React + Next.js. И способ найти их за 10 секунд.
Факт-чек: Как ИИ генерирует уязвимый код
🔍 Доказательства · Тренировочное смещение · Слепота к контексту
Согласно анализу GitHub публичных репозиториев, значительный процент утечек безопасности в React-проектах обусловлен общими паттернами в обучающих данных — такими как использование dangerouslySetInnerHTML без санитизации или хардкодинг переменных окружения на фронтенде.
В наших внутренних тестах WebValid ИИ-ассистенты (Cursor, Copilot, ChatGPT) успешно находили и исправляли уязвимости только тогда, когда им предоставляли структурированный отчет от внешнего сканера. Когда их просили «проверить этот код на безопасность» без контекста, модели пропускали 68% архитектурных утечек (таких как проблемы с бандлами API-ключей, описанные ниже).
dangerouslySetInnerHTML без очистки (санитизации)
🔴 Критично · Кража сессий, штрафы по GDPR · OWASP A03:2021 Injection
ИИ-ассистенты обожают этот паттерн. Вы просите «отрендерить HTML из API» — они пишут:
// ❌ Плохой код от ИИ
function UserBio({ bio }: { bio: string }) {
return <div dangerouslySetInnerHTML={{ __html: bio }} />;
}
Если bio приходит от пользователя — поздравляем, у вас XSS. Злоумышленник вставит <script>document.cookie</script> и украдет сессию вашего пользователя.
// ✅ Фикс: очистка через DOMPurify
import DOMPurify from "dompurify";
function UserBio({ bio }: { bio: string }) {
const sanitizedBio = DOMPurify.sanitize(bio);
return <div dangerouslySetInnerHTML={{ __html: sanitizedBio }} />;
}
Согласно поиску по публичным репозиториям GitHub,
dangerouslySetInnerHTMLбез очистки встречается в тысячах React-проектов. ИИ-ассистенты воспроизводят этот паттерн из обучающих данных, не задаваясь вопросом: «А откуда взялся этот HTML?».
API-ключи в клиентском бандле
🔴 Критично · Прямые финансовые потери · OWASP A02:2021 Cryptographic Failures
Самый распространенный грех Vibe Coding. ИИ-ассистент часто «исправляет» неработающий вызов API на стороне клиента, добавляя префикс NEXT_PUBLIC_ к вашим секретным ключам. Это заставляет запрос работать, но также вшивает ваш секрет прямо в общедоступный JavaScript-бандл, видимый всем через DevTools.
// ❌ Плохой код от ИИ — ключ утекает в JS-бандл
"use client";
const response = await fetch("https://api.openai.com/v1/chat/completions", {
headers: {
Authorization: `Bearer ${process.env.NEXT_PUBLIC_OPENAI_API_KEY}`, // ❌ УТЕЧКА
},
});
Фикс: Перенесите всю чувствительную логику в Server Action или Route Handler, где переменные окружения остаются строго на сервере.
Для глубокого погружения в механизмы утечек бандлов и использования продвинутой защиты, такой как Next.js Taint API, читайте наше руководство: Гид по утечкам API-ключей.
Если вы запускаете SaaS, ключ в JS-бандле виден в DevTools любого браузера, независимо от приватности репозитория. WebValid автоматически сканирует ваши публичные бандлы, чтобы обнаружить эти утечки раньше хакеров.
Невидимые проблемы требуют видимых отчетов
Такие инструменты, как WebValid, сканируют ваше приложение, чтобы выявить эти архитектурные ошибки, которые ИИ-инструменты часто упускают из виду.
Отсутствие заголовков безопасности (CSP, X-Frame-Options, HSTS)
🟠 Высоко · Clickjacking, кража токенов через MitM · OWASP A05:2021 Security Misconfiguration
ИИ-ассистенты не настраивают HTTP-заголовки. Это понятно — они пишут компоненты, а не конфиги серверов. В результате ваш сайт может быть встроен в <iframe> на вредоносном домене, что позволит провести атаку clickjacking.
Для Next.js + Vercel есть два способа добавить заголовки:
Метод 1 — next.config.js (для самостоятельного хостинга):
// ✅ next.config.js
const securityHeaders = [
{ key: "X-Frame-Options", value: "SAMEORIGIN" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{
key: "Content-Security-Policy",
value: "default-src 'self'; script-src 'self';",
},
];
module.exports = {
async headers() {
return [{ source: "/(.*)", headers: securityHeaders }];
},
};
Метод 2 — vercel.json (для деплоя на Vercel — рекомендуется):
{
"headers": [
{
"source": "/:path*",
"headers": [
{
"key": "Strict-Transport-Security",
"value": "max-age=63072000; includeSubDomains; preload"
},
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "SAMEORIGIN" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" }
]
}
]
}
Избегайте использования
script-src 'unsafe-inline'в вашем CSP — это нейтрализует защиту от XSS-атак через инлайн-скрипты. Если Next.js требует инлайн-скриптов — используйте nonce-based CSP. Для более надежной защиты от clickjacking используйтеframe-ancestorsвместоX-Frame-Options.
WebValid проверяет наличие всех этих заголовков в HTTP-ответе за один скан.
console.log с утечкой чувствительных данных в продакшен
🟡 Средне · Утечка PII во внешние агрегаторы логов · OWASP A09:2021 Security Logging Failures
Во время сессий Vibe Coding отладочные логи часто забываются в коде:
// ❌ Плохой код от ИИ (оставлен после итераций)
async function loginUser(credentials: Credentials) {
console.log("Login attempt:", credentials); // пароль в логах!
const user = await authService.login(credentials);
console.log("User logged in:", user); // токены в логах!
return user;
}
Если ваши серверные логи собираются такими инструментами, как Sentry, Datadog или Logtail — вы только что отправили пароли пользователей в сторонний сервис.
// ✅ Фикс: структурированное логирование только нечувствительных данных
import { createLogger } from "@your-scope/logger";
const logger = createLogger({ scope: "AuthService" });
async function loginUser(credentials: Credentials) {
logger.info("Login attempt", { email: credentials.email }); // только email
const user = await authService.login(credentials);
logger.info("Login successful", { userId: user.id }); // только ID
return user;
}
Это не гипотетическая проблема. Реальные случаи утечки паролей через логи случались в GitHub (2018), Twitter (2018) и Facebook (2019). ИИ-ассистенты не фильтруют данные — они генерируют именно то, что вы просили.
Сломанная семантика и ARIA
🟡 Средне · Потеря SEO-трафика, нарушения ADA · Нарушение доступности (WCAG 2.1)
ИИ-ассистенты делают всё кликабельным, вешая onClick на <div>. Это быстро и визуально работает, но убивает индексируемость и доступность:
// ❌ Плохой код от ИИ
function ProductCard({ product }: { product: Product }) {
return (
<div onClick={() => navigate(`/products/${product.id}`)}>
<div>{product.name}</div>
<div>{product.price}</div>
</div>
);
}
Проблемы:
- Скринридеры не распознают интерактивный элемент — пользователи с нарушениями зрения не могут перейти к продукту.
- Googlebot не строит граф ссылок через
onClick— только через<a href>. - Теряется передача веса ссылок (PageRank).
// ✅ Фикс: семантический HTML
function ProductCard({ product }: { product: Product }) {
return (
<article>
<a href={`/products/${product.id}`}>
<h2>{product.name}</h2>
<span>{product.price} ₽</span>
</a>
</article>
);
}
Google официально включил Core Web Vitals в сигналы ранжирования. Использование
<div onClick>вместо<a href>— это задокументированная потеря индексируемости и деградация вашего ссылочного графа.
Server Actions без авторизации
🔴 Критично · Выполнение действий от имени любого пользователя · OWASP A01:2021 Broken Access Control
ИИ-ассистенты часто генерируют Server Actions, но забывают добавить проверки прав доступа.
// ❌ Плохой код от ИИ
"use server";
export async function deleteAccount(userId: string) {
await db.user.delete({ where: { id: userId } });
}
Любой может отправить POST-запрос и удалить чужой аккаунт.
// ✅ Фикс: внутренняя проверка авторизации
"use server";
import { getServerSession } from "next-auth";
export async function deleteAccount(userId: string) {
const session = await getServerSession();
if (!session || session.user.id !== userId) {
throw new Error("Unauthorized");
}
await db.user.delete({ where: { id: userId } });
}
Как найти все 6 уязвимостей за 10 секунд
Вы могли бы проверять код вручную или потратить день на полный аудит. Но есть способ быстрее:
- Запустите локальный проект через ngrok, чтобы получить публичный URL.
- Вставьте его в WebValid.
- Получите готовый
ai-fix-promptв Markdown — вставьте его в свой ИИ-инструмент и исправьте всё за 2 минуты.
Ваш 6-пунктовый чек-лист по безопасности от генеративного ИИ
- Обработка ввода: Использовал ли ИИ
dangerouslySetInnerHTML? - Переменные окружения: Есть ли секретные ключи?
- Заголовки безопасности: Настроили ли вы
vercel.json? - Утечка данных: Есть ли прямые вызовы
console.log? - Интерактивный SEO: Проверьте кликабельные элементы.
- Авторизация действий: Начинается ли каждый экшен
'use server'с проверки авторизации?
Протестируйте свой проект бесплатно на WebValid
Официальная документация
Безопасность
Next.js
- Next.js: Security Headers
- Next.js: Environment Variables
- Next.js: Taint API
- Next.js: Server Actions Security