3,583 papers
arXiv:2610.08240 77 6 окт. 2026 г. FREE

Разрыв «работает, но дыряво»: новая модель пишет безопаснее в среднем, но не везде и не гарантированно

КЛЮЧЕВАЯ СУТЬ
Парадокс: новые модели в среднем пишут безопаснее, но ни одно семейство не закрыло разрыв между «тесты прошли» и «атаку выдержал». Исследование не даёт готовую технику. Оно даёт рабочую практику, как жить с этим разрывом при смене модели. Авторы просили модели дописать функцию без слов «пиши безопасно», как в обычной работе. Потом гоняли код на нормальных и на вредоносных входах. Две слабости не уходят у большинства моделей: подделка записей в логах через переводы строк и подмена заголовков HTTP-ответа. Новейшие проприетарные модели местами откатились на дырах, которых у предшественников уже не было. Среднее улучшение прячет регрессии. Вывод: смена модели обнуляет доверие к безопасности.
Адаптировать под запрос
⚡

TL;DR

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

Главная находка: апгрейд модели не гарантирует безопасность. В среднем новые версии пишут безопаснее, но ни одно семейство не закрыло разрыв. Часть слабостей не уходит вообще: log injection (подделка записей в логах через переводы строк) и HTTP response splitting (подмена заголовков ответа). Новейшие проприетарные модели местами откатились назад на уязвимостях, которые предшественники уже не допускали.

Ещё два вывода. «Открытая» не значит «менее безопасная»: все открытые семейства в исследовании заметно сузили разрыв, а флагман Google остался с широким. Дешёвая компактная версия обычно пишет менее безопасный код, чем флагман, но есть исключения. Авторы советуют перепрогонять проверки безопасности после каждой смены модели и класть безопасные функции (API) прямо в контекст промпта.

🔬

Схема метода

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

ШАГ 1: Меняешь модель/тариф → считаешь, что безопасность "обнулилась"
ШАГ 2: Даёшь модели в контексте безопасные функции проекта → пишет через них
ШАГ 3: Гоняешь атакующие проверки (особенно на логи и HTTP-заголовки) → сравниваешь с прошлой версией

Шаги 2–3 вы выполняете сами. Исследование показало, зачем они нужны, но готовых промптов не даёт.

🚀

Пример применения

Задача: Команда делает бэкенд интернет-магазина на Flask. Решили перейти с платной модели на открытую (Qwen или DeepSeek, запущенную у себя), чтобы данные не уходили за границу. Модель будет писать обработчики формы заказа. В них есть два типичных слабых места из исследования: логирование пользовательского ввода и редирект с заголовком из параметра запроса.

Промпт:

Ты пишешь код для бэкенда интернет-магазина на Python (Flask).

Доступные безопасные функции проекта. Используй ТОЛЬКО их для этих операций:
- core.safe_log.info(message, **fields) — логирование; сама экранирует переводы строк
- core.safe_http.redirect(url) — редирект; проверяет URL на запрещённые символы
- core.safe_http.set_header(name, value) — установка заголовка; блокирует CR/LF

Задача: напиши обработчик POST /order. Он принимает email, комментарий к заказу и параметр next
(куда вернуть пользователя после оформления). Нужно:
1. Залогировать оформление заказа вместе с email и комментарием.
2. Перенаправить пользователя на next.

Правила:
- Любые данные от пользователя (email, комментарий, next) считай недоверенными.
- Не используй logging.info, print и прямую запись заголовков через response.headers.
- После кода перечисли, какие пользовательские поля куда попадают (лог, заголовок, URL).

Результат: Модель выдаст обработчик, где логирование и редирект идут через ваши обёртки, а не через стандартные функции. Под кодом будет список маршрутов пользовательских данных (лог, заголовок, URL). По нему удобно проверять код глазами или передавать ревьюеру. Это снижает шанс, что модель сама напишет logging.info(f"... {comment}") с сырым вводом. Гарантии это не даёт: проверку всё равно нужно запустить.

🧠

Почему это работает

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

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

Что делать с разрывом. Среднее улучшение скрывает регрессии. Модель может стать лучше в целом и хуже на конкретной слабости. Поэтому проверки нужны именно после каждой смены модели, а не один раз при выборе.

Рычаги управления: - Список безопасных функций в промпте → расширяйте под свои слабые места (SQL, пути к файлам, десериализация). - Требование «перечисли, куда попадает ввод» → убери, если нужен только код. Оставь, если нужна проверка ревьюером. - Проверка после смены модели → замените ручной ревью на автоматические атакующие тесты на ваших типичных слабостях.

📋

Шаблон промпта

Это шаблон по мотивам выводов исследования. Самого промпта в статье нет.

Ты пишешь код на {язык} для {проект}.

Доступные безопасные функции проекта. Для этих операций используй ТОЛЬКО их:
- {функция_1} — {что_делает_и_от_чего_защищает}
- {функция_2} — {что_делает_и_от_чего_защищает}

Задача: {описание_задачи}

Правила:
- Все данные из {источники_ввода} считай недоверенными.
- Не используй {запрещённые_небезопасные_способы}.
- После кода перечисли, куда попадает каждое пользовательское поле: {места_риска}.

Что подставлять: - {функция} — реальные обёртки вашего проекта (или рекомендованные библиотеки) и одна фраза, от чего они защищают. - {запрещённые_небезопасные_способы} — прямые вызовы, которые нужно заменить: print, сырой os.path.join, eval. - {места_риска} — логи, HTTP-заголовки, пути к файлам, SQL-запросы, URL.

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

⚠️

Ограничения

⚠️ Только отдельные функции: Модель дописывает одну функцию по заготовке. Как ведут себя агенты в целом репозитории (например, Codex или Gemini CLI) исследование не проверяло.

⚠️ Один набор проверок: «Безопасный» здесь значит «прошёл заданный атакующий тест». Другие атаки и другие слабости могли остаться незамеченными.

⚠️ Без явной просьбы о безопасности: Авторы специально брали промпты без слов «пиши безопасно». Как меняется картина, если это попросить, тут не измеряли.

⚠️ Совет про безопасные функции в контексте не проверен экспериментом: Он сформулирован как вывод авторов в введении. Прямых замеров его эффекта в доступной части текста нет. Относитесь как к обоснованной рекомендации, а не к доказанному приёму.

⚠️ Результаты по языкам разные: Одна и та же слабость в разных языках даёт разный риск. В C уязвимостей больше всего почти у всех моделей. Выбирать модель «вообще» нельзя, нужно смотреть на свой язык.

⚠️ Текст разобран не полностью: Ниже учтены аннотация, введение и методология. Детальные таблицы по каждой модели и слабости в предоставленном фрагменте отсутствуют.

🔍

Как исследовали

Идея простая: предыдущая работа показала «модели умнее, но не безопаснее» на трёх семействах. Авторы решили проверить это шире. Они взяли 32 модели из семи семейств: два проприетарных (Google, OpenAI) и пять открытых (Qwen, DeepSeek, Z.ai, Moonshot, MiniMax). Для каждого семейства взяли три последовательные версии, в большинстве случаев флагман и компактный вариант.

Задачи взяли из набора CWEval: 119 задач, 31 тип слабостей, пять языков (C, C++, Go, JavaScript, Python). Модель получает заготовку функции с комментарием и дописывает тело. Потом код запускают дважды: функциональные тесты и атакующий вход, который проверяет, срабатывает ли уязвимость на деле. Это важно: не «похоже на уязвимый шаблон», а «реально сломался».

Для каждой задачи брали по 100 вариантов ответа. Всего получилось 380 800 программ. Больше вариантов даёт устойчивые оценки. Смотрели два режима: одна попытка (как у разработчика, принимающего первый вариант) и 50 попыток (может ли модель вообще выдать безопасное решение).

Что удивило. Во-первых, «открытость» не определяет траекторию: все открытые семейства сузили разрыв, а Gemini 3.1 Pro остался с таким же широким, как отмечали раньше у Llama. Во-вторых, сжатие разрыва в среднем идёт вместе с откатами в отдельных слабостях. Инсайт для практики: нельзя доверять среднему улучшению, нужно проверять на своих типичных ошибках.

💡

Адаптации и экстраполяции

🔧 Техника: собрать личный «набор слабых мест» → ловить регрессии при смене модели

Возьмите 5–10 задач из своего проекта, где есть риск (логирование ввода, заголовки, пути к файлам, SQL). Прогоняйте их через новую модель с одним и тем же промптом. Сравнивайте результат со старой моделью. Это мини-версия того, что авторы рекомендуют вендорам: сравнивать каждую новую версию с предыдущей по типам слабостей, а не по общему баллу.

Экстраполяция (идея автора разбора, не из исследования): перенесите блок «безопасные функции проекта» в файл инструкций агента.

## Безопасные функции (используй только их)
- Логи: core.safe_log.info(...) — не используй logging/print напрямую
- Редиректы и заголовки: core.safe_http.* — не пиши в response.headers напрямую
- Любой пользовательский ввод считай недоверенным и перед записью в лог/заголовок/путь проходи через эти функции

Так агент видит правило в каждой сессии, а не только когда вы вспомнили вставить его в промпт.

🔗

Ресурсы

  • Статья: Newer and Bigger, but Safer? A Longitudinal Study of the Functionality-Security Gap in LLM-Generated Code
  • Авторы: Thiago Santos de Moura, Fynn Matuschek, Flavio Toffalini, Yannic Noller — Ruhr-Universität Bochum (Германия)
  • Бенчмарк: CWEval (динамические проверки функциональности и безопасности на одной программе)
  • Предыдущая работа: Cannavale et al. — ввели понятие «functionality-security gap» и метрику доли небезопасных решений среди работающих (CVR)
  • Инструменты исследования: OpenRouter (API проприетарных моделей), vLLM (запуск открытых моделей)
  • Каталог слабостей: CWE (MITRE)

📋 Дайджест исследования

Ключевая суть

Парадокс: новые модели в среднем пишут безопаснее, но ни одно семейство не закрыло разрыв между «тесты прошли» и «атаку выдержал». Исследование не даёт готовую технику. Оно даёт рабочую практику, как жить с этим разрывом при смене модели. Авторы просили модели дописать функцию без слов «пиши безопасно», как в обычной работе. Потом гоняли код на нормальных и на вредоносных входах. Две слабости не уходят у большинства моделей: подделка записей в логах через переводы строк и подмена заголовков HTTP-ответа. Новейшие проприетарные модели местами откатились на дырах, которых у предшественников уже не было. Среднее улучшение прячет регрессии. Вывод: смена модели обнуляет доверие к безопасности.

Принцип работы

Принцип простой: не верь среднему, проверяй свои слабые места. Три шага. Поменял модель или тариф — считай, что проверка безопасности обнулилась. Дай модели в промпте готовые безопасные функции проекта и запрети обходные пути. Прогони атакующие тесты, особенно на логи и заголовки, и сравни с прошлой версией. Модель идёт по пути, который ей показали, а безопасный путь надо показать явно. Это как новый сотрудник: даёшь ему шаблоны и проверяешь первую неделю, а не полагаешься на красивое резюме. Шаги 2 и 3 делаешь сам. Готовых промптов в статье нет, шаблон ниже собран по мотивам выводов.

Почему работает

Модель учили выдавать код, который работает. Безопасность в запросе не упомянута, значит она не цель. Поэтому код спокойно проходит все функциональные тесты и ломается на атакующем входе. Модель берёт самый привычный способ. А привычный способ логировать пользовательский ввод или ставить заголовок часто дырявый. Код может быть на 100% рабочим и при этом дырявым: тесты на функции этого не покажут. Второй момент: у новой версии общий балл растёт, но конкретная слабость может ухудшиться. Поэтому проверка нужна после каждой смены модели, а не один раз при выборе. Ещё два вывода из сравнения. Открытые модели не хуже по безопасности: все открытые семейства в исследовании заметно сузили разрыв, а флагман Google остался с широким. Дешёвая компактная версия обычно пишет менее безопасный код, чем флагман, но бывают исключения. Честная оговорка: совет про безопасные функции в контексте авторы дают как вывод. Прямого замера его эффекта в доступной части текста нет.

Когда применять

Командная разработка на модели, которую вы меняете или собираетесь менять → особенно переход с платной на открытую, с флагмана на дешёвую версию или на свежий релиз. Лучше всего там, где код принимает ввод пользователя и пишет его в логи, заголовки, пути к файлам, запросы к базе. НЕ подходит как гарантия: исследование проверяло только отдельные функции, один набор атак и промпты без просьбы о безопасности. Как ведут себя агенты на целом репозитории, не измеряли. Результаты сильно зависят от языка: в C дыр больше всего почти у всех моделей, так что смотри на свой язык.

Мини-рецепт

1. Объяви смену модели тревогой: новая версия или новый тариф означает, что прошлые проверки не считаются.
2. Собери список слабых мест проекта: логи, заголовки ответа, пути к файлам, запросы к базе, десериализация.
3. Выбери безопасные функции: свои обёртки или рекомендованные библиотеки. К каждой допиши одну фразу, от чего она защищает.
4. Положи их в промпт: «для этих операций используй ТОЛЬКО их». Рядом запрети небезопасные способы: print, сырой os.path.join, eval.
5. Назови ввод недоверенным: одной строкой, перечислив источники: форма, параметры запроса, заголовки.
6. Попроси карту маршрутов: «после кода перечисли, куда попадает каждое пользовательское поле». Ревьюеру так проще. Не нужна карта — убери строку.
7. Прогони атакующие тесты: переводы строк в логах и заголовках, подставные пути, вредные URL. Сравни с результатом прошлой модели.
8. Автоматизируй: ручной просмотр глазами замени тестами на ваши типичные слабости, чтобы они шли на каждую смену модели.

Примеры

[ПЛОХО] : Напиши обработчик POST /order: залогируй email и комментарий, потом сделай редирект на параметр next
[ХОРОШО] : Ты пишешь бэкенд интернет-магазина на Flask. Безопасные функции проекта, для этих операций используй ТОЛЬКО их: core.safe_log.info(message, **fields) — логирование, экранирует переводы строк; core.safe_http.redirect(url) — редирект, проверяет URL на запрещённые символы; core.safe_http.set_header(name, value) — заголовок, блокирует CR/LF. Задача: обработчик POST /order. Принимает email, комментарий и next. Залогируй заказ с email и комментарием, перенаправь пользователя на next. Все данные от пользователя считай недоверенными. Не используй logging.info, print и прямую запись response.headers. После кода перечисли, куда попадает каждое пользовательское поле: лог, заголовок или URL. В первом случае модель выберет привычное logging.info(f"... {comment}") с сырым вводом. Это ровно та дыра с подделкой записей в логах. Во втором модель идёт через ваши обёртки и ещё выдаёт карту маршрутов ввода. Гарантии нет: атакующие тесты всё равно нужно запустить. Проверка после генерации: Найди в коде места, где недоверенный ввод попадает в лог, заголовок или файловый путь
Источник: Newer and Bigger, but Safer? A Longitudinal Study of the Functionality-Security Gap in LLM-Generated Code
ArXiv ID: 2610.08240 | Сгенерировано: 2026-10-07 06:00

Проблемы LLM

ПроблемаСутьКак обойти
Новая версия модели лучше в среднем, но хуже на отдельных слабых местахМеняешь модель на более новую или большую. Общее качество растёт. Но на некоторых конкретных случаях она пишет хуже предшественницы. Среднее число скрывает эти провалы. Особенно опасно для свойств, которые обычные тесты не видят. Например, код работает, но принимает вредоносный ввод. Так бывает с подделкой записей в логах через переводы строк и с подменой заголовков ответа. Часть таких слабостей не уходит даже у новых моделейНе считай, что апгрейд сохранил прежнюю проверенность. Собери свой набор проверок на самые опасные случаи. Для кода это атакующие входы: переводы строк в логах, служебные символы в заголовках. Прогоняй набор после каждой смены модели или тарифа. Сравнивай с результатом прошлой версии по каждому случаю отдельно, а не по общей оценке
📖 Простыми словами

Newer and Bigger, but Safer? A Longitudinal Study of the Functionality-Security Gap inLLM-Generated Code

arXiv: 2610.08240

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

Это как нанять строителя поставить входную дверь, и он сдаёт работу в срок: полотно висит ровно, ручка нажимается. Функциональный тест пройден на ура. Вот только вместо нормального ригельного замка мастер прикрутил детский шпингалет, потому что «замок в ТЗ явно не просили». Первая же проверка чужим ботинком — и вся защита разлетается в щепки.

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

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

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

Работа с исследованием

Адаптируйте исследование под ваши задачи или создайте готовый промпт на основе техник из исследования.

0 / 2000
~0.5-2 N-токенов ~10-30с
~0.3-1 N-токенов ~5-15с