Слепая зона безопасности.
ИИ-ассистенты выдают функциональный код, который молча опускает средства защиты, которые человек добавил бы по привычке — валидацию ввода, авторизацию, кодирование вывода, работу с секретами — потому что "оно компилируется и возвращает 200" выглядит как готово.
##Signs and Symptoms
Ревьюер замечает слепую зону безопасности, когда код работает на счастливом пути, но средства защиты, которые человек с настроем на безопасность добавляет рефлекторно, попросту отсутствуют. Модель демонстрирует функцию, а не защиту. Характерные признаки:
- Запросы / команды, собранные конкатенацией строк вместо параметризованных (точки внедрения SQL/команд/LDAP).
- Пользовательский ввод попадает в вывод без кодирования (отражённый/хранимый XSS) или в
fs/child_process/fetchбез белого списка (обход пути, SSRF). - Эндпоинт аутентифицирует, но никогда не авторизует — он проверяет, кто вы, но никогда — можете ли вы трогать эту запись (IDOR / нарушенная авторизация на уровне объектов).
- Захардкоженные секреты — ключи API, токены, пароли БД встроены "чтобы оно запустилось".
- Средства защиты, отсутствующие по упущению: нет CSRF-токена, нет заголовков безопасности, нет ограничения частоты, слабая/устаревшая криптография (MD5, SHA-1,
Math.random()для токенов). - Театр комментариев: комментарий
// validate inputили// TODO: add authсидит там, где должна быть настоящая проверка — OX обнаружила избыточные комментарии в 90–100% ИИ-кода, часто заменяющие отсутствующую логику.
// Сгенерированный ИИ обработчик Express — выглядит завершённым, выпускает три дыры
app.get("/api/orders/:id", authMiddleware, async (req, res) => {
const apiKey = "sk_live_8f3b2a91c7d44e0f"; // 1. захардкоженный секрет
const order = await db.query(
`SELECT * FROM orders WHERE id = ${req.params.id}` // 2. SQL-инъекция
);
res.send(`<h1>Order for ${order.customerName}</h1>`); // 3. отражённый XSS
// примечание: authMiddleware подтверждает вход, но никогда не проверяет order.userId === req.user.id (IDOR)
});
Четыре различные уязвимости, ноль падающих тестов — запах в том, чего нет.
##Reasons for the Problem
Почему модели это порождают
- Цель обучения — правдоподобие, а не безопасность. Предсказание следующего токена оптимизирует наиболее распространённый способ написания кода, а публичные корпуса насыщены сниппетами учебного уровня — с конкатенированными запросами и без авторизации. OX Security отмечает, что модели "по умолчанию выбирают небезопасные подходы, поскольку они чаще всего встречаются в обучающих данных". Carnegie Mellon обнаружил, что 61% ИИ-сниппетов работают, но лишь ~10,5% проходят ревью безопасности.
- "Готово" определяется счастливым путём. LLM оптимизирует так, чтобы видимый запрос завершился успехом. Средства защиты не дают положительного сигнала при быстром запуске, поэтому их отбрасывают — то, что OX называет "небезопасностью по неведению": функциональный код с отсутствующими мерами защиты, потому что никто из участников не знал, что требовалось.
- Нет контекста репозитория. Модель не видит вашу вспомогательную функцию
authorize(), вашу схему валидации или ваш менеджер секретов, поэтому встраивает литеральный ключ или пропускает проверку вместо переиспользования существующего средства защиты. - Подхалимство + скорость. Она возвращает буквально то, что вы попросили ("добавь эндпоинт заказа"), а не модель угроз вокруг него, а узкие места вроде ревью и моделирования угроз — это ровно то, что устраняет разработка на скорости ИИ — ключевой вывод OX в том, что именно скорость, а не частота дефектов на строку, отправляет уязвимый код в продакшен.
- Устаревание из-за порога обучения. Модели хватаются за криптографические и библиотечные паттерны, которые были приемлемы годы назад (MD5, SHA-1, устаревшие опции TLS, старые потоки аутентификации).
Почему это вредит
- Прямая эксплуатируемость. Независимые исследования, цитируемые OX: ~62% сгенерированного ИИ кода попадает в продакшен с уязвимостью; 86% провалили защиту от XSS (CSET/Georgetown); CodeRabbit измерил в 2,74 раза больше XSS, чем в человеческом коде; Tenzai обнаружил, что 0 из 15 построенных ИИ приложений выставляют заголовки безопасности или защиту от CSRF; Escape.tech нашёл 400+ открытых секретов в 5600 vibe-приложениях.
- Невидимо для тестов и для читателей. Отсутствующая проверка авторизации не имеет падающего теста и диффа, на который можно указать, поэтому она беспрепятственно проходит ревью — а объём вывода ИИ означает, что ревью не масштабируется, чтобы её поймать.
- Накапливающийся техдолг. Данные GitClear (рефакторинг упал с 25% до <10% изменённых строк, копипаста выросла и теперь превышает перемещённые строки, дублированные блоки выросли примерно в 8 раз) означают, что один и тот же небезопасный паттерн клонируется по кодовой базе, умножая радиус поражения любой отдельной слепой зоны.
##Treatment
Тактики ревью и промптинга
- Назовите модель угроз в промпте. "Напиши этот эндпоинт, считая, что
idконтролируется злоумышленником; обеспечь авторизацию на уровне объектов, параметризуй все запросы, кодируй весь вывод и читай секреты изprocess.env". Указание противника переворачивает поведение по умолчанию. - Принуждайте к переиспользованию существующих средств защиты. "Используй нашу вспомогательную функцию
authorize(user, 'order', id)и валидаторorderSchema— не пиши новую валидацию". Это прямо противодействует причине отсутствия контекста репозитория (и смежному запаху Duplicate Code). - Заставьте модель запускать инструменты. Требуйте, чтобы она запускала
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 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, загружается один раз при старте, никогда не встраивается
Там, где запах распространился клонированием, исправьте его один раз и Extract Function (Извлеките функцию) средства защиты (requireOrderOwnership, escapeHtml), чтобы каждое место вызова разделяло одну проверенную реализацию вместо N копий.
##Detected by
- Gitleaks hardcoded secrets / generic-api-key
- Semgrep p/owasp-top-ten (e.g. sql-injection, xss, dangerous formatstring sinks)
- 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 hardcoded_password_string, B608 hardcoded_sql_expressions, B324 weak hashlib
- SonarQube / SonarSource Security Hotspots & Vulnerability rules (injection, weak cryptography, hardcoded credentials)
- npm audit / OWASP Dependency-Check known-vulnerable dependency detection