ConstructiCat Logo
CodeBust.
Browse section ▾

Слепая зона безопасности.

ИИ-ассистенты выдают функциональный код, который молча опускает средства защиты, которые человек добавил бы по привычке — валидацию ввода, авторизацию, кодирование вывода, работу с секретами — потому что "оно компилируется и возвращает 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