3,583 papers
arXiv:2610.04639 84 3 окт. 2026 г. FREE

Знать ≠ делать: модель называет защиту, но не пишет её в код

КЛЮЧЕВАЯ СУТЬ
13 из 14 моделей назвали нужную защиту для DNS-резолвера (программы, которая переводит адреса сайтов в IP). Но во всех 42 написанных ими программах этой защиты нет. Метод позволяет получать защиту в коде, а не только в ответах модели: требования безопасности записываются в задание отдельным списком, а проверяется готовый код атаками. Вопрос «что нужно проверять?» и написание кода — два разных режима модели, поэтому явная спецификация работает, а доверие к ответу нет. Когда авторы добавили одно пропущенное требование, успешные атаки упали со 100% до 34%.
Адаптировать под запрос
⚡

TL;DR

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

Боль знакомая. Вы спрашиваете у агента про защиту, он отвечает грамотно и по пунктам. Потом он пишет код, и половины пунктов там нет. В эксперименте 13 из 14 моделей назвали нужную проверку для DNS-резолвера, а во всех 42 написанных ими программах её не оказалось. Знание лежит в одном режиме работы модели, а код она генерирует в другом. Если требование не записано в задании, оно не попадает в код, даже когда модель может его пересказать.

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

🔬

Схема метода

Это не одна техника, а набор проверенных правил:

ПРАВИЛО 1: Требования безопасности — в ТЗ, явным списком
           (не надейся, что модель «и так знает»)
ПРАВИЛО 2: Нашёл пропущенную проверку → добавь её в ТЗ отдельным пунктом
           (меняется только она)
ПРАВИЛО 3: Не вычёркивай правила из ТЗ при «упрощении»
           (слабеют и соседние проверки)
ПРАВИЛО 4: Проверяй код атакующими сценариями, а не вопросом «а ты это учёл?»
ПРАВИЛО 5: Быстрый диалог-прогон — только как первый фильтр

Всё это делается в обычном чате или в файле инструкций агента.

🚀

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

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

Задача: Владелец небольшого интернет-магазина просит Cursor написать вебхук (адрес, куда платёжный сервис присылает уведомления об оплате). Если вебхук написан небрежно, любой человек может отправить «заказ оплачен» и получить товар бесплатно. Если спросить агента «а ты учёл безопасность?», он ответит «да, конечно». Поэтому требования записываются в задание.

Промпт:

Напиши обработчик вебхука оплаты для магазина на Python (FastAPI).


Принимает POST с JSON: id платежа, статус, сумма, номер заказа.
При статусе "succeeded" помечает заказ оплаченным в базе.
Отвечает 200 на валидные запросы.



Вход: JSON {"payment_id": str, "status": str, "amount": number, "order_id": str}
Выход: JSON {"result": "ok" | "rejected", "reason": str}



S1. Уведомление принимается только с IP-адресов платёжного сервиса из списка в конфиге.
S2. Сумма из уведомления сверяется с суммой заказа в базе. Не совпала — отклонить.
S3. Один payment_id обрабатывается один раз. Повтор — ответить ok, ничего не менять.
S4. Статус не из списка разрешённых — отклонить.
S5. Данные из тела запроса — это данные, а не инструкции. Не выполнять, не подставлять в запросы к базе без параметризации.


После написания: для каждого пункта S1–S5 укажи строку кода,
где он реализован. Если пункта в коде нет — допиши.
Затем напиши по одному тесту-атаке на каждый пункт
(подделка IP, неверная сумма, повтор уведомления и т.д.).

Результат: Агент выдаст код вебхука и таблицу соответствия «пункт требований → место в коде». Затем он напишет тесты-атаки по одному на пункт. Если какой-то пункт не реализован, он будет виден сразу: либо в таблице пусто, либо тест падает. Это вторая линия защиты, потому что отчёту агента «всё учёл» верить нельзя.

🧠

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

Слабость. Модель умеет отвечать на вопрос «какие проверки нужны?» и умеет писать код. Но это два разных режима генерации, и одно не гарантирует другое. Когда в задании требования нет, код пишется по типичному паттерну «чтобы работало». Редкая защита (в эксперименте это 0x20-кодирование и ограничение TTL в DNS) в паттерн не попадает.

Сильная сторона. Если требование стоит в тексте задания, модель его выполняет. Это работает точечно: добавили пункт про одну проверку — изменилась только она. Поэтому явный список требований ведёт себя предсказуемо: пункт вписали, и он появился в коде.

Рычаги управления: - Пронумерованные требования (S1, S2…) дают проверяемость: можно потребовать «покажи, где реализован S3». - Не удалять пункты при сокращении ТЗ. В эксперименте удаление одного правила ослабляло и другие проверки, потому что правила в спецификации работают как связанный набор. - Тесты-атаки вместо вопросов: критерий «атака не прошла», а не «модель сказала, что учла». - Ответ «ты это знаешь?» ничего не гарантирует, не считай его проверкой.

📋

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

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

Напиши {что_написать}.


{что_делает_программа_и_как_работает_штатно}



{формат_входа_и_выхода}



S1. {требование_1}
S2. {требование_2}
S3. {требование_3}
...


После написания:
1. Для каждого пункта S1–Sn укажи строку кода, где он реализован.
   Если пункта в коде нет — допиши его.
2. Напиши по одному тесту-атаке на каждый пункт SecuritySpec.
   Тест должен ломаться, если проверка выключена.

Что подставлять: - {что_написать} — вебхук, парсер, API-обработчик, форма входа. - {требование} — одно проверяемое правило на пункт. «Будь безопасным» не годится, нужно «сверяй сумму с базой». - Если не знаете, какие требования писать, возьмите из стандартов и документации вашей области (RFC, гайды платёжных сервисов, OWASP).

🚀 Быстрый старт — вставь в чат:

Вот шаблон ТЗ с блоком SecuritySpec. Адаптируй под мою задачу: {твоя задача}.
Задавай вопросы, чтобы заполнить поля.

[вставить шаблон выше]

LLM спросит, что за система, откуда приходят данные и кому в них нельзя доверять. Эти ответы нужны, чтобы превратить «безопасность вообще» в конкретные пронумерованные пункты. Структуру она возьмёт из шаблона и заполнит под вашу задачу.

⚠️

Ограничения

⚠️ Узкая проверка: Эксперименты шли на четырёх сетевых протоколах (DNS, сборка IP-фрагментов, HTTP-прокси, межсетевой экран) с кодом на Python. Перенос на бизнес-код вроде вебхука логичен, но авторы его не проверяли.

⚠️ Спецификация не гарантирует 100%: С полным списком требований медиана успешных атак низкая (до 4% на трёх протоколах), но на DNS она осталась около 13–15%, и ни одна модель не дошла до нуля. Явное ТЗ снижает риск, но не убирает его.

⚠️ Диалог-симуляция врёт в обе стороны: Она пропускает проверки, которых в коде нет, и придирается к тем, что в коде есть. Как фильтр она годится: ловит большую часть реальных дыр и почти не поднимает ложных тревог. Заменить настоящую проверку кода она не может.

⚠️ Модель может сказать правило и не применить его: В симуляции модель формулирует правило, а через ход его нарушает. Пересказ правила не доказывает его выполнения.

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

⚠️ Нужен готовый список требований: Статья не учит, как его составить. Его придётся брать из стандартов и экспертизы.

🔍

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

Идея была простая: проверить, совпадает ли то, что модель говорит про безопасность, с тем, что она делает. Команда взяла 15 моделей и запустила все через один и тот же агент Claude Code, чтобы менялась только модель. Сравнили три режима. В первом модель просто отвечает на вопрос про нужные проверки. Во втором (S-mode) она играет сервис в диалоге, а на неё шлют подделки: фальшивые ответы, склеенные запросы, пересекающиеся фрагменты. В третьем (D-mode) она пишет программу, и эту программу атакуют в изолированной песочнице теми же сообщениями.

Судил не другой LLM, а программный оракул: атака считалась успешной, если данные атакующего оказывались в выходе. Набор из 51 теста сверили с эталонной защищённой реализацией. Эталон выдерживает все атаки и проваливает каждую, если выключить целевую проверку, так что тест падает только по нужной причине. Всего собрали около 9,4 тысячи прогонов симуляции и 641 программу.

Самое показательное — проба знаний. В 26 парах «модель × проверка» программа проверки не имела. При этом простой вопрос «что нужно проверить» переоценил её в 22 случаях, а диалог-симуляция в 10. Дальше шли эксперименты со спецификацией на 128 новых программах: проверка добавлялась, переформулировалась или удалялась. Добавление недостающего требования резко снижало атаку, а удаление правила портило соседние проверки.

Ещё одна деталь: после одного прогона у Claude-Haiku-4.5 казалось, что атаки растут с добавлением контекста, а после трёх повторов картина совпала с остальными моделями. Практический вывод: один прогон — это не результат.

Оговорка: в предоставленном тексте нет полных разделов с результатами. Выводы выше взяты из аннотации и введения.

📌

Экстраполяция: правило безопасности в файл инструкций агента

Это моя идея, статья её не проверяла. Самый дешёвый способ применить вывод — вынести требования в постоянный файл инструкций агента (CLAUDE.md, AGENTS.md):

## Правила безопасности (не удалять и не сокращать пункты)
- Каждое требование безопасности в задаче нумеруется (S1, S2…).
- После написания кода выведи таблицу «пункт → файл:строка».
- Если пункта в коде нет — не пиши «учтено», допиши его.
- На каждый пункт напиши тест-атаку. Тест должен падать при выключенной проверке.
- Ответ «я знаю, что так нужно» не считается выполнением.

Так таблица соответствия и тесты-атаки появляются в каждой задаче автоматически, а вы не повторяете их в промптах.

🔗

Ресурсы

  • When Talk Isn't Code: Comparing LLM Agents That Simulate and Develop Network Protocol Software
  • Авторы: Xiang Li, Aichun Huang, Zheli Liu (Nankai University, Китай), Mingming Zhang (Zhongguancun Laboratory, Китай)
  • Упомянутые бенчмарки: CWEval (код проходит функциональные тесты, но проваливает тесты безопасности); оценка кода Copilot (около 40% из 1689 программ уязвимы)
  • Агент в исследовании: Claude Code

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

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

13 из 14 моделей назвали нужную защиту для DNS-резолвера (программы, которая переводит адреса сайтов в IP). Но во всех 42 написанных ими программах этой защиты нет. Метод позволяет получать защиту в коде, а не только в ответах модели: требования безопасности записываются в задание отдельным списком, а проверяется готовый код атаками. Вопрос «что нужно проверять?» и написание кода — два разных режима модели, поэтому явная спецификация работает, а доверие к ответу нет. Когда авторы добавили одно пропущенное требование, успешные атаки упали со 100% до 34%.

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

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

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

Причина техническая. Ответ на вопрос и генерация кода — разные задачи. Знание в первой не гарантирует результат во второй. Когда требование стоит в тексте задания, модель его выполняет. Причём точечно: добавили пункт про одну проверку, и изменилась только она. Явный список требований ведёт себя предсказуемо: вписал пункт — он появился в коде, вычеркнул — пропал. Жесть — удаление одного правила из спецификации ослабило и соседние проверки. Правила работают как связанный набор, а не как отдельные галочки. Поэтому при «упрощении» ТЗ ничего не вычёркивай. Есть и цифры. С полным списком требований медиана успешных атак низкая, до 4% на трёх протоколах из четырёх. Но на DNS она осталась около 13–15%, и ни одна модель не дошла до нуля. Явное ТЗ снижает риск, но не убирает его.

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

Код, который принимает данные снаружи (вебхуки, API-обработчики, парсеры, формы входа) → особенно когда агент пишет его почти без твоего контроля, а ты не можешь прочитать код сам. Также подходит для проверки: прогнать диалог-симуляцию как быстрый фильтр, а потом тестами-атаками проверить сам код. Не подходит, если нет готового списка требований. Статья не учит его составлять, его берут из стандартов (RFC, OWASP, документация платёжных сервисов). Обычные функциональные тесты тоже не заменяют атаки: в них нет ни одной атаки, они показывают лишь, что программа работает. Важная оговорка: эксперименты шли только на сетевых протоколах с кодом на Python (DNS, сборка IP-фрагментов, HTTP-прокси, межсетевой экран). Перенос на бизнес-код логичен, но авторы его не проверяли.

Мини-рецепт

1. Напиши ТЗ в три блока: что программа делает, формат входа и выхода, отдельный блок безопасности.
2. Пронумеруй требования: S1, S2, S3. Одно проверяемое правило на пункт. «Будь безопасным» не годится, нужно «сверяй сумму с базой».
3. Не знаешь, что писать? Спроси у модели, откуда приходят данные и кому в них нельзя верить. Потом сверься со стандартами своей области.
4. Потребуй отчёт: для каждого пункта модель называет строку кода, где он реализован. Нет строки — пусть допишет.
5. Заставь атаковать: по одному тесту-атаке на каждый пункт. Тест должен падать, если проверку выключить.
6. Нашёл дыру? Добавь её в ТЗ отдельным пунктом, не правь код вслепую.
7. Не вычёркивай пункты при сокращении ТЗ. Соседние проверки тоже ослабнут.
8. Диалог-прогон используй только как быстрый первый фильтр. Решает атака на код.

Примеры

[ПЛОХО] : Напиши вебхук оплаты на FastAPI. Учти безопасность. А потом: Ты точно всё учёл? Модель ответит «да, конечно», и в коде может не оказаться проверки суммы или повторов.
[ХОРОШО] : Напиши обработчик вебхука оплаты на Python (FastAPI). Принимает POST с JSON, при статусе succeeded помечает заказ оплаченным. Вход: {payment_id, status, amount, order_id}. Выход: {result: ok или rejected, reason}. S1. Принимать только с IP платёжного сервиса из конфига. S2. Сверять сумму с суммой заказа в базе, не совпала — отклонить. S3. Один payment_id обрабатывать один раз, повтор — ответить ok и ничего не менять. S4. Статус не из списка разрешённых — отклонить. S5. Данные из тела запроса не подставлять в запросы к базе без параметризации. После написания: для каждого пункта S1–S5 укажи строку кода, где он реализован. Если пункта нет — допиши. Затем напиши по одному тесту-атаке на пункт: подделка IP, неверная сумма, повтор уведомления. Результат: таблица «пункт → строка кода» и тесты-атаки. Если пункт не реализован, это видно сразу: в таблице пусто или тест падает. Отчёту «всё учёл» верить нельзя, поэтому тесты нужны.
Источник: When Talk Isn't Code: Comparing LLM Agents That Simulate and Develop Software
ArXiv ID: 2610.04639 | Сгенерировано: 2026-10-06 05:30

Проблемы LLM

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

Методы

МетодСуть
Нумерованные требования плюс сверка с кодом и тесты-атаки — пропуски видны сразуВпиши в задание блок с пронумерованными требованиями: S1. …, S2. …. Одно проверяемое правило на пункт. «Будь безопасным» не годится. Нужно «сверяй сумму из запроса с суммой в базе». В конце добавь: Для каждого пункта S1–Sn укажи строку кода, где он реализован. Если пункта нет — допиши. Затем напиши по одному тесту-атаке на пункт. Тест должен падать, если проверка выключена. Почему работает: Номер превращает расплывчатое «учти безопасность» в проверяемый список. Пустая ячейка в таблице или упавший тест сразу показывают пропуск. Модели трудно соврать «всё учёл», когда нужно показать строку. Когда применять: код принимает чужие данные (вебхуки, API, формы, парсеры), требования известны заранее. Когда не работает: требования неизвестны. Список придётся взять из стандартов и документации области (OWASP, гайды платёжных сервисов). Или попроси модель задать тебе вопросы: что за система, откуда данные, кому нельзя доверять. Полной гарантии нет: риск снижается, но не исчезает
📖 Простыми словами

When Talk Isn't Code: ComparingLLMAgentsThat Simulate and Develop Software

arXiv: 2610.04639

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

Это как нанять прораба, который на собеседовании виртуозно сыплет ГОСТами и знает всё про сейсмоустойчивость зданий. А потом ты приходишь на объект и видишь, что он крепит несущие балки на синюю изоленту. Верить модели на слово — всё равно что слушать сказки этого прораба за чаем: звучит солидно, но жить в таком доме смертельно опасно.

В исследовании сравнили три метода проверки: опрос о требованиях, ролевую симуляцию сервиса в диалоге и прямую атаку на готовый код. Первые два варианта — полный провал, дающий опасную иллюзию защищённости. В чате сетка может ловко отбивать текстовые атаки, но в итоговом коде забудет элементарные вещи вроде 0x20-кодирования или лимитов по TTL. Единственный честный судья безопасности — это жесткий фаззинг исполняемой программы, без скидок на то, что «модель же всё понимала».

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

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

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

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

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