Утекшие API-ключи: Как AI подставляет джунов при генерации кода
Стек: React, Next.js App Router, Vite. Проблема: утечка секретных ключей и токенов в клиентские JS-бандлы через неправильное использование переменных окружения в ИИ-генерируемом коде.
Введение
Представьте обычное утро соло-разработчика: кофе, открытый редактор и радость от того, что новая интеграция искусственного интеллекта в SaaS-продукт работает безупречно. Vibe Coding в действии: вы попросили ассистента написать клиентский компонент React, он выдал код, данные рендерятся. Вы делаете push в мастер и идете спать.
А утром вы просыпаетесь и видите счет от Google Cloud на $82,314. За 48 часов. Причина? Всего одна строка ИИ-сгенерированного кода упаковала секрет в публичный бандл. Такая утечка API ключей стала одной из главных проблем индустрии.
Искусственный интеллект кардинально ускорил разработку. Но у больших языковых моделей есть фундаментальная слепая зона, описанная в кейсе аудита Garry Tan: они оптимизируют код для текущего файла, абсолютно не понимая архитектурной разницы между безопасным сервером и публичным клиентом. Когда вы просите ИИ «быстро подключить оплату» или «сделать запрос к базе данных с фронтенда», он генерирует работающий код. Но при этом молча сливает ваши секреты. И если вы не проводите аудиты клиентского кода, потеря данных инфраструктуры Next.js или Vite — это лишь вопрос времени.
Кейс-стади: Тысячи ключей Google Cloud в открытом доступе
🔴 Критично · Финансовые потери · OWASP A02:2021 Cryptographic Failures
Масштаб проблемы выходит за рамки единичных ошибок джунов. В начале 2026 года эксперты по безопасности из Truffle Security опубликовали пугающее исследование. Они просканировали исходный код публичных сайтов и обнаружили 2 863 активных ключа Google Cloud, вшитых прямо в клиентский JavaScript (обычно они начинаются с префикса AIza).
Самое страшное в этом кейсе — механизм «Retroactive Privilege Expansion» (ретроактивное расширение привилегий). Долгие годы Google рекомендовал использовать эти ключи для безопасных публичных сервисов вроде Google Maps. Разработчики смело вшивали их во фронтенд, так как ключи не давали доступа к приватным данным. Но когда в проектах начали массово включать API генеративного ИИ Gemini, все старые «публичные» ключи автоматически получили права на запуск тяжелых inference-моделей. Безобидный ключ для карт превратился в безлимитную кредитку для хакеров, которые парсили эти ключи из JS-бандлов.
Это подтверждает и отчет GitGuardian “State of Secrets Sprawl 2026”: за один 2025 год в публичный код GitHub утекло 28,65 миллионов новых захардкоженных секретов (рост на 34% за год). При этом коммиты, созданные с помощью ИИ-инструментов, текут ровно в два раза чаще, чем код, написанный человеком. ИИ не заботится о безопасности; его задача — заставить код работать прямо сейчас.
Анатомия утечки
🔴 Критично · Компрометация инфраструктуры · OWASP A02:2021 Cryptographic Failures
Почему ИИ-ассистенты так легко сливают данные? Всё сводится к тому, как современные фронтенд-фреймворки (Next.js, Vite) управляют переменными окружения. Чтобы переменная попала в браузер, фреймворк требует специальный префикс: NEXT_PUBLIC_ для Next.js или VITE_ для Vite.
Во время Vibe Coding происходит следующий классический сценарий:
- Вы просите ИИ написать клиентский компонент, который делает запрос к стороннему API (например, OpenAI или Stripe).
- ИИ пишет
fetch, используя стандартныйprocess.env.SECRET_KEY. - В браузере запрос падает, потому что
process.env.SECRET_KEYна клиенте равенundefined. Вы вставляете ошибку обратно в чат. - ИИ-ассистент видит ошибку «переменная не найдена на клиенте» и радостно предлагает решение: «Просто добавьте префикс
NEXT_PUBLIC_в ваш.envфайл!».
Вы делаете это. Запрос начинает работать. ИИ спас день. Но под капотом вебпак или бандлер Vite только что вшил ваш секретный ключ строковым литералом прямо в скомпилированный .js файл.
// ❌ AI-hallucination: Типичный сгенерированный код, утекающий в клиентский бандл
export default function CheckoutButton() {
const handlePayment = async () => {
const res = await fetch("https://api.stripe.com/v1/charges", {
headers: {
// УТЕЧКА: Ключ попадет прямо в минифицированный JS
Authorization: `Bearer ${process.env.NEXT_PUBLIC_STRIPE_SECRET_KEY}`,
},
});
};
return <button onClick={handlePayment}>Оплатить</button>;
}
Любой посетитель может нажать F12, открыть вкладку Network или Sources и достать ключ. Подобные утечки — это именно то, что сканер бандлов WebValid должен отлавливать до того, как ваша сборка попадет в продакшен.
Проекты на Vite: Ловушка VITE_
В Vite механизм аналогичен. Только переменные с префиксом VITE_ доступны в клиентском коде. Если ИИ-ассистент сталкивается с ошибкой undefined в компоненте Vite, он порекомендует добавить префикс:
// ❌ Сгенерированная ИИ утечка в Vite
// .env: VITE_SUPABASE_KEY=your_secret_key
const supabase = createClient(
process.env.VITE_SUPABASE_URL,
process.env.VITE_SUPABASE_KEY,
);
Хотя это и работает, процесс сборки Vite выполняет статическую замену. Загляните в папку dist/, и вы найдете что-то вроде:
const supabase = createClient("https://xyz.supabase.co", "your_secret_key");
Дополнительная защита — Next.js Taint API:
Если вы используете Next.js 14+ (App Router), вы можете использовать экспериментальный Taint API, чтобы явно запретить передачу конфиденциальных объектов клиенту. Это создает «жесткий блок», который даже ИИ-ассистенты не смогут случайно обойти:
// 1. next.config.js
module.exports = { experimental: { taint: true } };
// 2. server-only-logic.ts
import { experimental_taintObjectReference } from "react";
export function getSecureConfig() {
const config = { apiKey: process.env.STRIPE_SECRET_KEY };
experimental_taintObjectReference(
"Не передавайте секретный конфиг клиенту",
config,
);
return config;
}
Если ИИ попытается передать этот объект config клиентскому компоненту, React выдаст ошибку рендеринга, предотвращая утечку в зародыше.
// ✅ Исправление: Переносим запрос в Server Action или Route Handler
"use server"; // Код выполняется строго на сервере
export async function processPayment() {
const res = await fetch("https://api.stripe.com/v1/charges", {
headers: {
// БЕЗОПАСНО: Ключ без префикса живет только в Node.js среде
Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}`,
},
});
}
Фатальные ошибки ИИ-ассистентов с секретами
🟠 Высокий · Потеря данных пользователей · OWASP A05:2021 Security Misconfiguration
Слепое доверие ИИ при настройке инфраструктуры приводит к нескольким типичным ошибкам, наблюдаемым в тысячах проектов.
Создание публичных файлов .env.example
Ассистенты часто генерируют файл-шаблон .env.example для документации проекта. Но они обучались на огромных массивах данных GitHub и могут непреднамеренно использовать реальные сигнатуры токенов или даже строки, которые вы упоминали ранее в сессии, в качестве «заполнителей».
# ❌ Опасный сгенерированный ИИ .env.example
# ИИ может использовать формат реального ключа, который он недавно обработал
STRIPE_SECRET_KEY=sk_live_51PZ... # ИИ может слить здесь реальный ключ
Конфигурации Firebase и Supabase
ИИ часто генерирует код инициализации прямо в файлах на стороне клиента (например, root-layout.tsx). Он сбрасывает туда объекты конфигурации, не проверяя, защищен ли бэкенд правилами Row Level Security (RLS).
// ❌ Опасный код ИИ в клиентском компоненте
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, // ❌ Раскрыт без проверки RLS
);
Путаница между Server и Client компонентами
В Next.js ИИ часто создает серверный компонент, который правильно читает секреты, а затем добавляет onClick или useState по вашему запросу. Это принудительно добавляет директиву 'use client' в файл, и внезапно те переменные, которые были безопасны на сервере, попадают в клиентский код.
// ❌ ИИ рефакторит это в 'use client' для поддержки кнопки
"use client";
export default function SecretPage({ data }) {
// Если 'data' содержит секреты, они теперь в пропсах публичного JS-бандла
return <button onClick={() => console.log(data)}>Показать данные</button>;
}
Безопасность «под капотом»: Как устроена проверка бандла
Чтобы понять, почему автоматизация необходима, стоит взглянуть на то, как именно происходит утечка. Когда вы запускаете сборку, бандлер (Webpack или Turbopack) собирает все зависимости в огромные JS-файлы. Если ИИ-ассистент «протащил» туда секрет, он останется там в виде обычной строки.
Вы можете убедиться в этом сами, проинспектировав артефакты сборки. Это навык, который должен знать каждый разработчик, заботящийся о безопасности:
- Запустите сборку:
npm run build. - Перейдите в папку скомпилированного кода (
.next/static/chunks/в Next.js илиdist/в Vite). - Используйте
grepилиripgrepдля поиска известных сигнатур секретов:
# Поиск сигнатур: Stripe (sk_), Google Cloud (AIza), JWT (ey...)
grep -r -E "sk_test_|sk_live_|AIza|ey[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\." .next/static/
Подобный ручной аудит — отличный способ «прощупать» проблему. Однако в реальности полагаться на него опасно: регулярные выражения могут давать ложные срабатывания, а человеческий фактор (забыли запустить скрипт перед деплоем) слишком велик. Именно здесь на сцену выходит профессиональная автоматизация.
Автоматический сканер утечек в JS-бандле
Инспектировать терминал и писать regex перед каждым релизом — это то, что разработчики часто забывают делать в спешке. Vibe Coding предполагает скорость. Аудит тоже должен быть мгновенным.
| Возможность | WebValid Security Scanner |
|---|---|
| Скан ключей в JS-бандле | ✅ Анализирует AST и текст бандлов на паттерны секретов |
| Заголовки безопасности | ✅ Проверяет CSP, HSTS, X-Frame-Options |
| Broken semantics / ARIA | ✅ Анализирует финальный HTML |
| Логика авторизации | ❌ Требует мануального ревью бизнес-логики |
| Статический анализ исходников | ❌ Проверяет только собранный продакшен-бандл |
Вам достаточно скомпилировать проект и передать URL в публичный сканер. Если ИИ оставил ключи от Stripe, OpenAI или Google Cloud в публичном доступе, сканер не просто укажет на это, а выдаст полный Markdown-отчет с ai-fix инструкциями.
Искусственный интеллект отлично пишет функциональный код — он просто не знает, в какой момент архитектуры свернул не туда и положил пароль в публичную папку. Дайте ему карту ошибок, и он исправит всё сам. Узнайте, как превратить эти отчеты в готовые ИИ-задачи.
Ваш чек-лист безопасности API из 5 пунктов
Прежде чем пушить новую генеративную фичу в продакшен, проведите быструю ручную проверку:
- Grep-ните сборку: Используйте
grep -r "sk_"илиgrep -r "AIza"в вашей папке.next/илиdist/. - Аудит префиксов: Проверьте ваши
.envфайлы. Есть ли у секретов префиксыNEXT_PUBLIC_илиVITE_? - Проверка интерактивности: Не добавил ли ИИ
'use client'в серверный компонент, работающий с переменными окружения? - Проверка фикстур: Не отдаете ли вы
.env.exampleили тестовые моки в продакшен. - Обзор экшенов: Используют ли ваши Server Actions
getServerSession(или аналог) для проверки принадлежности данных?
Получите готовый промпт для исправления ИИ за 20 секунд. Без конфигов. Бесплатный скан. → https://webvalid.dev/
Есть вопросы по результатам аудита? Свяжитесь с нами