К содержимому

IT · PRO

Безопасность кода, написанного ИИ: типовые дефекты и что проверять на ревью

· 13 мин чтения · Редакция ultrathink

Код, написанный ИИ, ломается не случайным образом, а по узнаваемым шаблонам: выдуманные или подменённые зависимости, секреты прямо в коде, устаревшие API, пропущенные проверки прав и небезопасные настройки по умолчанию. Модель пишет правдоподобный код, а не заведомо безопасный, поэтому ревью должно целенаправленно искать именно эти дефекты. Ниже — что это за дефекты, откуда они берутся, как их ловить и как выстроить процесс, в котором они не доходят до продакшена.

Почему у ИИ-кода свои типовые уязвимости

Языковая модель воспроизводит то, что часто встречалось в обучающих данных. А в открытом коде много учебных примеров, где безопасность намеренно опущена ради краткости, и много устаревших решений, которые когда-то были нормой. Модель не знает вашей модели угроз, ваших правил доступа и того, какие версии библиотек у вас стоят.

Вторая причина — уверенность. Код выглядит аккуратно, снабжён комментариями и проходит тесты, которые написал тот же агент. Из-за этого ревьюер расслабляется и пропускает то, что заметил бы в небрежном коде коллеги.

Третья — агент редко задаёт неудобные вопросы. Человек, получив задачу «сделать выгрузку пользователей», спросит, кому эта выгрузка доступна и какие поля в неё нельзя включать. Агент чаще просто сделает выгрузку. Поэтому требования безопасности нужно либо явно писать в задаче, либо закрепить в правилах проекта, которые агент читает всегда.

Модель угроз: откуда приходят проблемы

Полезно разделять три источника риска, потому что против каждого работают разные средства. Если смешивать их, легко закрыть один и оставить открытыми два других.

  • Дефекты в самом коде. Модель написала уязвимую логику: инъекцию, пропущенную проверку прав, небезопасную настройку. Против этого — ревью, тесты и статический анализ.
  • Цепочка поставки. В проект попадает чужой код через зависимости, которые предложила модель. Против этого — проверка пакетов, lock-файлы и сканеры зависимостей.
  • Сам агент. Инструмент с доступом к репозиторию, терминалу и сети может сделать лишнее по ошибке или под влиянием вредоносных инструкций в данных. Против этого — минимальные права и изоляция.

Дальше разберём каждый класс дефектов, а в конце соберём их в процесс и чек-лист.

Выдуманные и подменённые зависимости

Как это возникает

Модель может уверенно предложить установить пакет, которого не существует, — просто потому что такое название звучит правдоподобно для задачи. Злоумышленники знают об этом и регистрируют пакеты с такими именами в публичных реестрах, чтобы код с вредоносной нагрузкой попал в проекты при установке. Близкая угроза — опечатки в названиях популярных пакетов и пакеты-двойники с похожим описанием.

Опасность в том, что вредоносный код может выполниться уже при установке, ещё до того, как вы запустите проект и поймёте, что пакет не делает того, что обещал.

Как проверять

  1. 01Смотрите в диффе каждое изменение файла зависимостей и lock-файла — это отдельный пункт ревью.
  2. 02Для нового пакета проверьте: существует ли он в реестре, совпадает ли имя с тем, что вы ожидаете, кто его поддерживает, давно ли он опубликован, насколько он используется, есть ли у него репозиторий с историей.
  3. 03Спросите, нужна ли зависимость вообще: часто задачу решает уже подключённая библиотека или несколько строк кода.
  4. 04Запретите агенту устанавливать пакеты без подтверждения.
  5. 05Включите сканер зависимостей в CI, чтобы известные уязвимые версии не попадали в основную ветку.

Секреты в коде, логах и контексте модели

ИИ охотно подставляет ключ API прямо в код «для примера», а затем этот пример уезжает в репозиторий. Бывает и обратное: агент читает файл с переменными окружения, чтобы отладить ошибку, и выводит его содержимое в лог, в отчёт или в комментарий. Всё, что попало в контекст модели, нужно считать переданным за пределы вашей машины в соответствии с политикой поставщика.

  • Держите секреты вне репозитория, в менеджере секретов или переменных окружения, и закройте агенту доступ к файлам с ними.
  • Включите сканирование секретов в pre-commit и в CI.
  • Проверяйте, что код не пишет в логи токены, пароли, заголовки авторизации и персональные данные.
  • Следите за сообщениями об ошибках: подробная ошибка с параметрами подключения к базе в ответе API — тоже утечка.
  • Для разработки с агентом используйте отдельные тестовые ключи с минимальными правами.

Если утечка уже случилась

  1. 01Отзовите скомпрометированный ключ и выпустите новый — это первое действие, а не последнее.
  2. 02Проверьте журналы использования ключа у поставщика сервиса: были ли обращения, которых вы не ожидали.
  3. 03Обновите ключ во всех окружениях, где он использовался, и убедитесь, что старый больше нигде не работает.
  4. 04Очистите историю репозитория, если это требуется политикой, понимая, что это не отменяет первого шага.
  5. 05Разберите, как ключ попал в код, и закройте путь: правило в файле инструкций агента, сканер в pre-commit, отдельные тестовые ключи.

Устаревшие API, криптография и небезопасные значения по умолчанию

У модели есть дата среза знаний, и о новых версиях библиотек она может не знать. В итоге появляются вызовы функций, которые уже удалены или признаны небезопасными, устаревшие алгоритмы хеширования и шифрования, старые способы настройки фреймворков.

  • Хеширование паролей быстрыми хеш-функциями общего назначения вместо специальных алгоритмов для паролей.
  • Собственная реализация шифрования или подписи вместо проверенной библиотеки.
  • Генерация токенов и кодов подтверждения обычным генератором случайных чисел вместо криптографически стойкого.
  • Сравнение секретов обычным сравнением строк там, где нужно сравнение за постоянное время.
  • Устаревшие параметры TLS или отключённая проверка сертификата.

Отдельная категория — изменения «чтобы заработало»: разрешённые запросы с любого домена, включённый режим отладки, отключённая защита от подделки запросов, широкие права на файлы. Агент часто делает такие шаги, когда упирается в ошибку, и честно пишет об этом в отчёте одной строкой, которую легко пропустить. Любое отключение защитного механизма в диффе — повод остановиться и спросить, зачем оно здесь.

Инъекции и обработка входных данных

Модели в целом знают о параметризованных запросах, но в спешке, в редких библиотеках и в «быстрых» скриптах всё равно собирают запросы и команды склейкой строк. Особенно это заметно в коде, который выглядит служебным: отчёты, админские страницы, скрипты миграции данных.

  • SQL-запросы, собранные из пользовательского ввода, включая динамические имена колонок для сортировки.
  • Команды оболочки с подстановкой имён файлов или параметров из запроса.
  • Пути к файлам из пользовательского ввода без нормализации: выход за пределы разрешённой папки.
  • Вывод пользовательских данных в HTML без экранирования или через функции, которые вставляют сырой HTML.
  • Запросы сервера по адресу, который передал пользователь, без ограничения допустимых адресов.
  • Десериализация данных из недоверенного источника форматами, которые умеют создавать произвольные объекты.

Условный пример. Агент добавляет к отчёту сортировку и передаёт имя колонки из параметра запроса прямо в текст SQL — параметризовать имя колонки нельзя, а другого способа он не придумал. Тесты проходят, сортировка работает. Правильное решение — белый список допустимых колонок, и его нужно либо указать в задаче, либо поймать на ревью.

Как ловить на ревью

Инъекции хорошо ищутся по признакам. Просматривайте в диффе места, где строка запроса, команды или пути собирается из переменных; вызовы, которые запускают процессы оболочки; функции, вставляющие сырой HTML; серверные запросы по адресу из параметров. Часть этих признаков ловят правила статического анализа, и их стоит включить в CI. Для важных эндпоинтов полезны тесты с заведомо вредным вводом: кавычками, путями с переходом в родительскую папку, разметкой в текстовых полях.

Авторизация и изоляция данных

Самые опасные дефекты не видны сканерам, потому что синтаксически код корректен. Эндпоинт возвращает объект по идентификатору и не проверяет, принадлежит ли он текущему пользователю. Фильтр по организации есть во всех запросах, кроме одного нового. Роль проверяется в интерфейсе, а сервер выполняет действие для любого, кто знает адрес.

Модель решает функциональную задачу и часто не знает о правилах доступа, если их не описали явно. Она видит соседний код и может повторить проверку, а может и не повторить — например, если проверка сделана в промежуточном слое, который не виден из файла, где она работает.

  • Для каждого нового эндпоинта спросите: кто может его вызвать и к каким данным он даёт доступ?
  • Проверьте, что при чтении и изменении по идентификатору проверяется владелец или организация.
  • Убедитесь, что проверка прав выполняется на сервере, а не только скрывает кнопку в интерфейсе.
  • Посмотрите на массовые операции и выгрузки: они часто обходят проверки, которые есть в одиночных запросах.

Условный пример

В проекте есть эндпоинт просмотра счёта, где идентификатор берётся из адреса, а проверка принадлежности счёта организации пользователя выполнена в отдельном промежуточном обработчике маршрутов. Агент добавляет новый эндпоинт скачивания счёта в PDF, копирует логику загрузки по идентификатору, но регистрирует маршрут в другом месте, где этот обработчик не подключён. Функционально всё работает, тесты на скачивание проходят. Дефект обнаружит только негативный тест: пользователь другой организации запрашивает чужой счёт и должен получить отказ.

Тихие дефекты: ошибки, логи и ослабленные тесты

Есть класс проблем, который не выглядит как уязвимость, но создаёт её. Агент, стремясь сделать код «надёжным», оборачивает операцию в обработчик, который ловит любую ошибку и ничего не делает. Сбой проверки прав или записи в журнал аудита становится невидимым, а система продолжает работать так, будто всё в порядке.

  • Пустые или слишком широкие обработчики ошибок, которые скрывают сбой.
  • Ошибки, которые возвращают пользователю внутренние детали: трассировку, запрос к базе, пути к файлам.
  • Логирование всего объекта запроса или ответа вместе с персональными данными и токенами.
  • Тесты, которые агент изменил, чтобы они прошли: ослабленные проверки, пропущенные случаи, изменённые ожидаемые значения.
  • Удалённые или отключённые проверки в CI, которые мешали агенту завершить задачу.

Изменения в тестах и в конфигурации CI стоит просматривать отдельно от изменений в коде и с особым вниманием. Если агент поменял тест, в описании должно быть объяснено, почему изменилось требование, а не просто что тест «починен».

Сам агент как поверхность атаки

Агент читает не только ваш код, но и всё, что ему попадается: описания задач, комментарии, документацию, ответы внешних сервисов, содержимое веб-страниц. Если в этих текстах спрятаны инструкции, агент может им последовать, — это называют инъекцией в промпт. Например, комментарий в чужом пакете или текст в задаче от внешнего пользователя может попросить агента отправить куда-то содержимое файла или добавить «полезную» зависимость.

Полностью защититься от этого на уровне модели нельзя, поэтому защита строится на уровне прав. Важно, что агент может сделать, если поверит вредной инструкции.

  • Ограничьте набор команд, которые агент выполняет без подтверждения.
  • Ограничьте сетевой доступ там, где он не нужен для задачи.
  • Не давайте агенту боевых ключей, доступа к продакшену и к данным клиентов.
  • Запускайте фоновых агентов в изолированной среде, где худший сценарий — испорченная ветка.
  • Требуйте ревью человеком перед слиянием любого изменения, независимо от того, кто его подготовил.

Полезно также сохранять журнал действий агента: какие команды он выполнил, какие файлы читал, к каким адресам обращался. При разборе инцидента такой журнал показывает, было ли поведение агента результатом задачи или посторонней инструкции, и помогает понять, какие права ему на самом деле нужны.

Как встроить проверки в процесс

Ревьюер не должен в одиночку ловить то, что ловит автоматика. Механическую часть стоит переложить на конвейер, а внимание человека оставить для того, что машина не видит.

  1. 01До коммита: сканирование секретов и форматирование.
  2. 02В CI: проверка типов, линтеры с правилами безопасности, статический анализ, сканер зависимостей, все тесты.
  3. 03Правило слияния: нельзя слить изменение с красным CI или без одобрения человека.
  4. 04Ревью: логика доступа, обработка входных данных, уместность решений, изменения в тестах и зависимостях.
  5. 05Владельцы критичных частей: изменения в аутентификации, платежах, правах доступа и работе с персональными данными требуют одобрения ответственного за эту часть.

Разная строгость для разных изменений

  • Если изменение касается только интерфейса и не трогает данные — обычного ревью и CI достаточно, потому что ущерб от ошибки ограничен.
  • Если изменение добавляет эндпоинт, запрос к базе или обработку файлов — нужны негативные тесты на доступ и проверка ввода, потому что здесь возникают инъекции и утечки.
  • Если изменение затрагивает аутентификацию, деньги, права или персональные данные — ревью ответственного за эту часть обязательно, потому что ошибка здесь стоит дороже всего.

Второе правило — держать дифф обозримым. Большой пул-реквест от агента невозможно внимательно прочитать, и уязвимость теряется среди сотен строк форматирования. Небольшие изменения с понятной целью проверяются заметно надёжнее.

Требования безопасности в правилах проекта

Часть дефектов дешевле предотвратить, чем ловить. В постоянный файл инструкций для агента стоит записать правила безопасности проекта: все запросы к данным фильтруются по организации; проверка прав — только на сервере; запросы к базе — только через принятый слой доступа с параметрами; новые зависимости — только с согласования; секреты не читать и не выводить; защитные механизмы не отключать без явного указания в задаче. Агент будет следовать им не всегда, но заметно чаще, чем правилам, которых он не видел.

Чек-лист ревью кода, написанного ИИ

Этот список удобно вставить в шаблон пул-реквеста, чтобы ревьюер проходил его осознанно, а не по памяти.

  1. 01Новые зависимости: существуют, совпадают по имени, поддерживаются, действительно нужны.
  2. 02Lock-файл: изменения соответствуют заявленным зависимостям.
  3. 03Секреты: нет ключей и паролей в коде, конфигурации, тестах и логах.
  4. 04Доступ: каждый запрос к данным ограничен текущим пользователем или организацией.
  5. 05Проверки прав выполняются на сервере, включая массовые операции и выгрузки.
  6. 06Ввод: запросы параметризованы, команды не собираются из строк, пути нормализованы.
  7. 07Вывод: пользовательские данные экранированы.
  8. 08Защитные механизмы: ничего не отключено «чтобы заработало».
  9. 09Криптография: используются стандартные библиотеки и актуальные алгоритмы.
  10. 10API: вызовы актуальны для ваших версий библиотек.
  11. 11Ошибки: не проглатываются и не раскрывают внутренние детали пользователю.
  12. 12Тесты: не ослаблены, есть негативные сценарии для прав доступа.
  13. 13CI: проверки не отключены и не ослаблены.
  14. 14Объём: дифф не содержит изменений за пределами задачи.

Вывод

ИИ не делает код небезопасным сам по себе, но смещает риски в предсказуемые места: зависимости, секреты, устаревшие решения, проверки доступа и тихие обходы защит. Автоматика — сканеры зависимостей и секретов, линтеры, CI — закрывает механическую часть, а логику доступа и разумность решений проверяет человек.

Если знать типовые дефекты, искать их целенаправленно и ограничить права самого агента, код, написанный с помощью ИИ, можно доводить до того же уровня, что и код опытной команды. Ответственность за этот уровень по-прежнему лежит на людях.

Частые вопросы

Сам по себе — не гарантированно: модель пишет правдоподобный код и может повторять небезопасные шаблоны. Такой код становится приемлемым после такого же или более строгого ревью и автоматических проверок, что и код людей.

Это пакеты, которые модель предлагает установить, хотя их не существует или они не те, за кого себя выдают. Злоумышленники могут зарегистрировать такие имена, поэтому каждую новую зависимость нужно проверять вручную.

Типичны пропущенные проверки доступа, инъекции из-за склейки строк, секреты в коде и логах, устаревшие алгоритмы и отключённые защитные механизмы. Самые опасные из них — ошибки авторизации, потому что сканеры их не видят.

Лучше нет. Агенту нужна среда без боевых ключей и паролей; всё, что попало в его контекст, следует считать переданным поставщику модели.

Сканеры зависимостей и секретов, статический анализ, строгие линтеры и обязательные проверки в CI. Логику доступа они не заменяют — её проверяет человек на ревью.

Это инструкции, спрятанные в данных, которые читает агент: в комментариях, документации, задачах или ответах внешних сервисов. Защита строится на ограничении прав агента, а не на надежде, что модель их проигнорирует.

Читайте также

Все статьи
Заявка

Начнём с разговора.

Оставьте контакт — свяжемся, разберём вашу задачу и честно скажем, какой уровень вам подходит. Если не подходит ни один — так и скажем.

Или напишите напрямую

Отвечаем в рабочее время в течение 30 минут.

Что интересует

Без предоплаты и без обязательств. Отказаться можно на любом шаге.

Нажимая кнопку, вы соглашаетесь на обработку персональных данных в соответствии с политикой конфиденциальности.