Слепая зона безопасности.
ИИ-ассистенты выдают функциональный код, который молча опускает средства защиты, которые человек добавил бы по привычке — валидацию ввода, авторизацию, кодирование вывода, работу с секретами — потому что "оно компилируется и возвращает 200" выглядит как готово.
##Признаки и симптомы
Ревьюер замечает слепую зону безопасности, когда код работает на счастливом пути, но средства защиты, которые человек с настроем на безопасность добавляет рефлекторно, попросту отсутствуют. Модель демонстрирует функцию, а не защиту. Характерные признаки:
- Запросы / команды, собранные конкатенацией строк вместо параметризованных (точки внедрения SQL/команд/LDAP).
- Пользовательский ввод попадает в вывод без кодирования (отражённый/хранимый XSS) или в
fs/child_process/fetchбез белого списка (обход пути, SSRF). - Эндпоинт аутентифицирует, но никогда не авторизует — он проверяет, кто вы, но никогда — можете ли вы трогать эту запись (IDOR / нарушенная авторизация на уровне объектов).
- Захардкоженные секреты — ключи API, токены, пароли БД встроены "чтобы оно запустилось".
- Средства защиты, отсутствующие по упущению: нет CSRF-токена, нет заголовков безопасности, нет ограничения частоты, слабая/устаревшая криптография (MD5, SHA-1,
Math.random()для токенов). - Театр комментариев: на месте реальной проверки стоит комментарий
// validate inputили// TODO: add auth. OX Security обнаружил избыточные построчные комментарии («Comments Everywhere») в 90–100% изученного кода, сгенерированного ИИ; при этом запахе такой комментарий часто заменяет собой отсутствующую логику.
// Сгенерированный ИИ обработчик Express — выглядит завершённым, выпускает четыре дыры
app.get("/api/orders/:id", authMiddleware, async (req, res) => {
const apiKey = "sk_live_8f3b2a91c7d44e0f"; // 1. захардкоженный секрет
const { rows: [order] } = await db.query(
`SELECT * FROM orders WHERE id = ${req.params.id}` // 2. SQL-инъекция
);
res.send(`<h1>Order for ${order.customerName}</h1>`); // 3. отражённый XSS
// 4. IDOR: authMiddleware подтверждает вход, но никогда не проверяет order.userId === req.user.id
});
Четыре различные уязвимости, ноль падающих тестов — запах в том, чего нет.
##Причины проблемы
Почему модели это порождают
- Цель обучения — правдоподобие, а не безопасность. Предсказание следующего токена оптимизирует под самый распространённый способ писать код, а публичные корпуса переполнены учебными сниппетами со склейкой SQL-запросов из строк и без авторизации. В статье OX Security о безопасности вайб-кодинга это сказано прямо: «Конкатенация строк чаще всего встречается в обучающих данных». В бенчмарке SusVibes Университета Карнеги — Меллона на 200 реальных задачах по разработке функциональности SWE-Agent с Claude 4 Sonnet выдавал функционально корректное решение примерно в 61% случаев, а безопасное — лишь примерно в 10,5%.
- «Готово» определяется счастливым путём. LLM оптимизирует под то, чтобы видимый запрос выполнился успешно. Средства защиты не дают никакого положительного сигнала при быстром прогоне — отсутствующая проверка не роняет ни одного теста и не ломает демо, — поэтому они выпадают.
- Нет контекста репозитория. Модель не видит вашу вспомогательную функцию
authorize(), вашу схему валидации или ваш менеджер секретов, поэтому встраивает литеральный ключ или пропускает проверку вместо переиспользования существующего средства защиты. - Угодливость + скорость. Модель возвращает ровно то, что вы попросили («добавь эндпоинт заказа»), а не модель угроз вокруг него, а узкие места вроде ревью и моделирования угроз — именно то, что устраняет разработка на скорости ИИ. Главный вывод OX Security: в сгенерированном ИИ коде не больше уязвимостей на строку, чем в написанном человеком; уязвимый код в продакшен отправляет скорость, потому что «полноценное ревью кода просто не может масштабироваться под новую скорость выдачи». OX также предупреждает о приложениях, «небезопасных по глупости» (insecure by dumbness): их собирают с помощью ИИ и выкатывают нетехнические пользователи, у которых нет экспертизы в безопасности, чтобы заметить, чего не хватает.
- Устаревание из-за порога обучения. Модели хватаются за криптографические и библиотечные паттерны, которые были приемлемы годы назад (MD5, SHA-1, устаревшие опции TLS, старые потоки аутентификации).
Почему это вредит
- Прямая эксплуатируемость. Отчёт Veracode 2025 GenAI Code Security Report (80 задач, более 100 LLM) зафиксировал уязвимости в 45% случаев, а защититься от межсайтового скриптинга (CWE-80) модели не смогли в 86% случаев. CodeRabbit обнаружил XSS-уязвимости в пул-реквестах, написанных при участии ИИ, в 2,74 раза чаще, чем в написанных только людьми; Tenzai выяснил, что ни одно из 15 приложений, собранных пятью агентами, не имело защиты от CSRF и не выставляло заголовки безопасности; Escape.tech нашёл более 400 раскрытых секретов в более чем 5600 приложениях, написанных вайб-кодингом.
- Невидимо для тестов и для читателей. Отсутствующая проверка авторизации не имеет падающего теста и диффа, на который можно указать, поэтому она беспрепятственно проходит ревью — а объём вывода ИИ означает, что ревью не масштабируется, чтобы её поймать.
- Нарастающий техдолг. Анализ GitClear 2025 года, охвативший 211 млн изменённых строк, показал, что доля строк, связанных с рефакторингом («перемещённых»), упала с 25% изменённых строк в 2021 году до менее чем 10% в 2024-м, тогда как доля скопированных строк выросла с 8,3% до 12,3% и в 2024 году впервые превысила долю перемещённых; число блоков из пяти и более дублирующихся строк за 2024 год выросло примерно в восемь раз. Один и тот же небезопасный шаблон клонируется по всей кодовой базе и умножает радиус поражения каждой слепой зоны.
##Лечение
Тактики ревью и промптинга
- Назовите модель угроз в промпте. "Напиши этот эндпоинт, считая, что
idконтролируется злоумышленником; обеспечь авторизацию на уровне объектов, параметризуй все запросы, кодируй весь вывод и читай секреты изprocess.env". Указание противника переворачивает поведение по умолчанию. - Принуждайте к переиспользованию существующих средств защиты. "Используй нашу вспомогательную функцию
authorize(user, 'order', id)и валидаторorderSchema— не пиши новую валидацию". Это прямо противодействует причине отсутствия контекста репозитория (и смежному запаху Дублирование кода). - Заставьте модель запускать инструменты. Требуйте, чтобы она запускала
semgrep --config p/owasp-top-ten,gitleaks detect,npm auditи ваш конфигeslint-plugin-security, затем исправляла то, что они отметят, прежде чем объявлять о готовности. - Явно спрашивайте об отрицательных моментах. "Перечисли вопросы аутентификации, авторизации, валидации ввода, кодирования вывода и работы с секретами для этого обработчика и покажи, где каждый из них обеспечен". Пробел в этом списке и есть ошибка.
- Моделируйте угрозы для диффа, а не для строки. Опасные дыры — это упущения, поэтому проводите ревью, спрашивая "какое средство защиты должно быть здесь и которого нет?", а не выискивая плохие строки.
Рефакторинг — параметризуйте запрос (убивает инъекцию), кодируйте вывод (убивает XSS), добавьте проверку авторизации на уровне объекта (убивает IDOR) и перенесите встроенный секрет в конфигурацию:
// ПОСЛЕ
app.get("/api/orders/:id",
authMiddleware,
validate(orderParamsSchema), // валидация ввода
async (req, res) => {
const { rows: [order] } = await db.query(
"SELECT * FROM orders WHERE id = $1", [req.params.id] // параметризовано
);
if (!order || order.userId !== req.user.id) { // авторизация на уровне объектов
return res.sendStatus(404);
}
res.json({ customerName: order.customerName }); // структурированный вывод, автоматически кодируется
}
);
// секрет: process.env.STRIPE_API_KEY, загружается один раз при старте, никогда не встраивается
Там, где запах распространился клонированием, исправьте его один раз и примените к средству защиты (requireOrderOwnership, escapeHtml) Извлечение метода, чтобы все места вызова пользовались одной проверенной реализацией вместо N копий.
##Обнаружение анализаторами
- Gitleaks generic-api-key — Секреты, заданные в коде / generic-api-key
- Semgrep p/owasp-top-ten — p/owasp-top-ten (например, sql-injection, xss, опасное использование строк форматирования)
- GitHub CodeQL js/sql-injection, js/xss, js/hardcoded-credentials, js/path-injection
- eslint-plugin-security detect-non-literal-fs-filename, detect-child-process, detect-eval-with-expression, detect-object-injection
- Bandit (Python) B105 / B608 / B324 — B105 hardcoded_password_string, B608 hardcoded_sql_expressions, B324 слабый hashlib
- SonarQube / SonarSource Security Hotspots / Vulnerability — Правила проверки потенциальных угроз и уязвимостей (инъекции, слабая криптография, учётные данные в коде)
- npm audit / OWASP Dependency-Check npm audit / OWASP Dependency-Check — Обнаружение зависимостей с известными уязвимостями