Prompt injection проти coding agent: загрози й захист
Дізнайтеся, як текст із README, pull request і tool output стає інструкцією для coding agent, які поверхні атаки існують і який захист справді зменшує шкоду.
Що таке prompt injection проти coding agent
Prompt injection проти coding agent має просте визначення: текст, який читає агент, сприймається як інструкція, яку агент виконує. Агент відкриває файл, коментар до pull request, вебсторінку або результат виклику інструмента. Усе це надходить як текст того самого типу, що й ваш власний запит. Якщо зловмисник контролює будь-який фрагмент цього тексту, він може впливати на ваш сеанс.
Таку властивість мають усі сучасні продукти для агентів. Модель отримує одну послідовність токенів. Ваш запит, system prompt, вміст файлів і результати роботи інструментів об’єднуються, після чого модель прогнозує продовження. Для токена не існує ознаки привілеїв. Формат не визначає, яку частину авторизували ви, а яка надійшла з чужого README-файлу.
Ця сторінка описує модель загроз: як текст під контролем зловмисника потрапляє до агента, що працює на сервері, які можливості отримує зловмисник на кожному етапі та які засоби захисту виправдовують витрати. Рекомендації щодо ізоляції в інших наших посібниках мають сенс лише після того, як ви зрозумієте, від чого саме потрібно ізолювати агента.
Чому модель не може відокремити вміст від інструкцій
Навчання допомагає, але не вирішує проблему. Сучасні моделі навчають обережно ставитися до отриманого тексту, і вони відхиляють багато грубих спроб. Відмова є ймовірністю, а не правилом. Зловмисник може перефразовувати запит, повторювати спроби й приховувати текст у форматі, якого ніхто не передбачив. Кількість таких формулювань нічим не обмежена.
Проєкт OWASP GenAI відстежує цю проблему як LLM01:2025 Prompt Injection і розділяє її на два типи. Пряма ін’єкція — це зміна поведінки моделі за допомогою власного запиту користувача. Непряма ін’єкція — це зміна поведінки під впливом зовнішнього вмісту, наприклад вебсайту або файлу, під час його оброблення моделлю. На сервері важлива саме непряма ін’єкція, оскільки агент читає значно більше тексту, ніж ви вводите.
Перше систематичне дослідження виконали Greshake та його колеги: Not what you've signed up for (2023). Їхній висновок варто запам’ятати: коли застосунок передає отриманий текст моделі, яка може викликати інструменти, оброблення цього тексту стає майже еквівалентним довільному виконанню коду.
Умова, за якої читання перетворюється на компрометацію
Саме по собі читання шкідливого тексту не завдає шкоди. Для завдання шкоди потрібен канал виведення даних із машини.
Simon Willison назвав таке поєднання летальною тріадою у червні 2025 року. Агент, який має доступ до приватних даних, взаємодіє з ненадійним вмістом і може передавати дані назовні, можна змусити вивести перші через третій компонент.
Coding agent на вашому VPS має все це відразу. Приватні дані — це ваш вихідний код, файл .env, ключі SSH і історія shell. Ненадійний вміст — це кожен репозиторій, сторінка та результат роботи інструментів, які він читає. Каналом виведення можуть бути git push, curl, npm publish, тіло pull request або посилання, надруковане в терміналі, яке ви відкриєте.
Ви не можете усунути другу умову, оскільки читання ненадійного тексту — це завдання, для якого ви найняли агента. Тому кожен практичний захист має працювати з двома іншими умовами.
Де недовірений текст потрапляє до coding agent на сервері
Репозиторій, у якому працює agent
Кожен файл у checkout є вхідними даними. Коментарі у вихідному коді, README.md, журнали змін, тестові фікстури, vendored code і самі файли інструкцій для agent: CLAUDE.md, AGENTS.md та їхні аналоги. Agent, якого попросили зрозуміти codebase, читає їх, оскільки саме цього від нього очікують.
Тут зловмисник отримує доступ до всіх, хто клонує репозиторій і запускає в ньому agent. Файли інструкцій є найпрямішим шляхом, оскільки їх створено саме для читання як інструкцій. Pull request, що додає чотири корисні рядки до CLAUDE.md і один рядок із перенаправленням agent, може залишитися непоміченим під час швидкого перегляду людиною.
Issues, pull requests і коментарі code review
Будь-який текст, який стороння людина може ввести у ваш tracker, потрапляє до agent одразу після того, як ви просите його виконати triage. У травні 2025 року Invariant Labs опублікувала висновок щодо GitHub MCP саме такого типу. Agent розробника мав доступ до одного публічного та приватних репозиторіїв. Зловмисник створив issue у публічному репозиторії. Коли розробник попросив agent переглянути відкриті issues, agent прочитав вміст приватних репозиторіїв і записав його в pull request у публічному репозиторії.
У цьому звіті описано архітектурну проблему, а не дефект коду MCP server. Agent мав один токен із широкими правами доступу, читав із публічної скриньки та мав дозвіл на запис. У звичайному розумінні нічого не було налаштовано неправильно, тому відповіддю має бути обмеження прав, а не виправлення коду.
Вебсторінки, які завантажує agent
Документація, відповіді на форумі, сторінка vendor, результат пошуку. Будь-який із цих матеріалів може містити текст, написаний для agent, а не для вас. Перетворення HTML на текст дає зловмиснику більше можливостей, оскільки до моделі потрапляє навіть вміст, який браузер ніколи не відображає.
Зловмисник отримує контроль у момент, коли за agent стежать найменше. Ніхто не читає повний текст сторінки, яку agent завантажив під час пошуку інформації.
Вивід інструментів MCP
MCP (model context protocol) — поширений спосіб підключення agent до зовнішніх інструментів. Результати повертаються як текст і безпосередньо потрапляють у context window. Тут є дві поверхні атаки, а не одна. Очевидна поверхня — дані, які повертає інструмент. Інша — власні назва та опис інструменту, які модель читає, щоб вирішити, коли його викликати; server, яким ви не керуєте, може змінити будь-яке з них між викликами.
Зловмисник, який додає текст до виводу одного інструменту, отримує доступ до всіх інших інструментів agent. Так ін’єкція в інструменті з низькою цінністю зрештою керує інструментом із високою цінністю.
CI logs, build output і метадані залежностей
npm install виводить текст із пакетів, які ви не писали. Помилка тесту виводить повідомлення assertion з бібліотеки. Журнал завдання continuous integration (CI) містить тисячі рядків виводу сторонніх компонентів. Якщо попросити agent виправити невдалу збірку, він прочитає все це.
Тут зловмисник отримує доступ до build machine, де зазвичай зберігаються облікові дані для розгортання та registry tokens і за якою зазвичай стежать менше, ніж за laptop.
Що насправді отримує зловмисник
Варто планувати чотири можливі наслідки.
Викрадення облікових даних. Усе, що може прочитати процес агента, перебуває в зоні ризику: змінні середовища, ~/.aws/credentials, ~/.ssh, токен gh, файл конфігурації Docker. Для їхнього передавання назовні не потрібен curl. Коміт у гілку, опис pull request, пакет, опублікований у registry, або DNS-запит до імені, контрольованого зловмисником, — усі ці дії передають дані за межі сервера.
Зміни коду, які ви схвалюєте. Написання коду — завдання агента, тому змусити його додати непомітно неправильний рядок є найпростішим сценарієм. Наприклад, можна додати залежність або виклик журналювання, який передає токен у журнал, що ви надсилаєте в інше місце.
Закріплення в системі. Файл, записаний один раз, продовжує працювати без участі моделі: hook у .git/hooks, скрипт postinstall у package.json, рядок, доданий до файлу запуску оболонки, або додатковий рядок у CLAUDE.md. Наступна команда виконає його.
Переміщення всередині мережі. Агент працює там, де ви його розмістили. Якщо цей сервер має доступ до бази даних через loopback, внутрішнього адміністративного сервісу, metadata service вашого cloud provider або іншого хоста в приватній мережі, такий самий доступ має все, що керує агентом.
Режими автоматичного схвалення усувають останню перевірку
У режимі за замовчуванням Claude Code запитує підтвердження перед виконанням команди або редагуванням файлу. Цей запит є перевіркою з боку людини перед кожною з описаних вище дією. Режими без запиту усувають цю перевірку.
У документації прямо зазначено щодо bypassPermissions: використовуйте його лише в ізольованих середовищах, наприклад у контейнерах або віртуальних машинах, де Claude Code не може завдати шкоди. Auto mode працює обережніше: він автоматично схвалює виклики інструментів і паралельно виконує фонові перевірки безпеки, які визначають, чи відповідають дії вашому запиту. Ці перевірки виявляють багато проблем. Проте це все одно оцінювання виводу моделі самою моделлю, тому сприймайте їх як фільтр, а не як межу захисту.
Адміністратор може вимкнути обидві перевірки. Установіть permissions.disableBypassPermissionsMode або permissions.disableAutoMode у значення "disable" у файлі налаштувань і розмістіть цей файл у керованих налаштуваннях, щоб клонований проєкт не міг їх перевизначити. У нашому посібнику про auto mode і правила дозволів Claude Code описано, де саме застосовується кожне правило.
Засоби захисту в порядку їхньої користі
Жоден із цих засобів не є повним виправленням. Кожен або обмежує те, що агент може отримати, або те, що він може з цим зробити.
- Машина, яку можна знищити й відновити, щоб компрометація коштувала вам години, а не інциденту.
- Облікові дані, окремі від ваших, обмежені одним репозиторієм і короткоживучі.
- Відсутність довгоживучих секретів у середовищі, яке успадковують команди агента.
- Обмеження операційної системи для вихідного мережевого трафіку та доступу до файлів, які застосовуються до кожного процесу, запущеного агентом.
- Увімкнені запити на підтвердження для запису та мережевих викликів.
- Хуки як детермінований резервний захист для конкретних дій, які можна назвати.
- Перевірка diff перед його злиттям.
Порядок має значення. Пункти 1–4 діють навіть тоді, коли модель повністю контролює зловмисник. Пункти 5–7 залежать від уваги людини, а саме вона зникає під час тривалого запуску агента.
Запускайте агента на машині, яку можна викинути
VPS із checkout і одним токеном з обмеженими правами є значно менш цінною ціллю, ніж ноутбук із вашими ключами. Запускайте агента від імені окремого непривілейованого користувача, а не свого облікового запису для входу і не root. У наших посібниках про disposable VM для coding agents і про користувачів із мінімальними привілеями на VPS описано налаштування, а в посібнику про безпечний запуск Claude Code на VPS — щоденну роботу в такій конфігурації.
Приберіть секрети із середовища
Змінну середовища може прочитати кожен дочірній процес, тобто кожна команда, яку запускає агент. Sandbox Claude Code може скасовувати задані змінні перед кожною командою в sandbox. У Linux спочатку потрібні два пакети:
sudo apt-get install bubblewrap socatПотім у ~/.claude/settings.json:
{
"sandbox": {
"enabled": true,
"network": {
"allowedDomains": ["github.com", "*.npmjs.org"]
},
"credentials": {
"envVars": [
{ "name": "GITHUB_TOKEN", "mode": "deny" },
{ "name": "NPM_TOKEN", "mode": "deny" }
]
}
}
}Запис deny скасовує цю змінну перед запуском кожної команди в sandbox, а allowedDomains обмежує команди в sandbox списком указаних хостів. Блок credentials потребує Claude Code версії 2.1.187 або новішої; перевірено у серпні 2026 року. Виконайте /sandbox у сеансі, щоб побачити активні рівні та відсутні залежності. Визначити, які секрети взагалі мають бути на цій машині, — це більша половина роботи. Посібник про недопущення секретів у зону досяжності AI-агента допоможе це організувати.
Обмежте вихідний мережевий трафік на рівні операційної системи
Правило firewall не залежить від рішення моделі. Запускайте агента від імені окремого користувача agent, а потім блокуйте трафік, який цей користувач надсилає:
table inet agentcage {
chain output {
type filter hook output priority filter; policy accept;
meta skuid "agent" ct state established,related accept
meta skuid "agent" oif lo accept
meta skuid "agent" counter drop
}
}У результаті користувач agent матиме доступ лише до loopback. Тому його трафік має проходити через proxy на цій самій машині, а proxy зберігатиме allowlist імен хостів. Коли https_proxy указує на цей proxy, клієнт надсилає запит CONNECT, а proxy виконує пошук імені. Отже, агенту не потрібен власний вихідний DNS (domain name system). Перевірте результат за допомогою sudo nft list ruleset і стежте, як зростає лічильник у правилі блокування, коли агент намагається звернутися до нового ресурсу.
Під час зміни правил firewall залишайте відкритим другий SSH-сеанс. Також перевірте, як ваш container runtime обробляє ці правила: Docker створює власні ланцюжки, а в матеріалі про те, як published Docker ports обходять ufw описано причину цієї несподіваної поведінки.
Хуки: перевірка, яку модель не може обійти переконуванням
Правила дозволів і хуки застосовує Claude Code, а не модель. У документації це сформульовано прямо: інструкції у вашому prompt або CLAUDE.md визначають, що Claude намагається зробити, але не змінюють того, що дозволяє Claude Code. У цьому й полягає основна цінність розмежування. Рядок у CLAUDE.md із текстом "never run curl" є лише рекомендацією, з якою може сперечатися ін’єкований абзац. Хук — це процес, який повертає код завершення.
Зареєструйте хук PreToolUse у .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/no-egress.sh"
}
]
}
]
}
}Хук отримує виклик інструмента у форматі JSON через стандартне введення. Код завершення 2 блокує виклик і показує Claude причину зі стандартного потоку помилок. Код завершення 0 дає змогу виклику продовжити виконання через звичайний процес перевірки дозволів.
#!/usr/bin/env bash
# PreToolUse: stdin holds the tool call, exit 2 blocks it.
cmd=$(jq -r '.tool_input.command // ""')
if printf '%s' "$cmd" | grep -qE '(^|[;&|]|\s)(curl|wget|nc|ncat)(\s|$)'; then
echo "Blocked: this repository does not allow outbound network commands." >&2
exit 2
fi
exit 0Тепер про обмеження. Це denylist для shell-рядка, а denylist для shell-рядків можна обійти. python3 -c відкриває socket, не використовуючи слово curl. Ціль make deploy може приховати той самий виклик ще на один рівень. Створюйте хуки для помилок, які можна чітко назвати, а межу, на яку ви фактично покладаєтеся, реалізуйте в kernel або в мережі.
Правила deny для дозволів також мають важливе обмеження зіставлення. Правила Read і Edit блокують власні файлові інструменти Claude та файлові команди, які він розпізнає в Bash, наприклад cat, head, tail і sed. Вони не блокують Python- або Node-скрипт, який сам відкриває файл. Правила перевіряються в такому порядку: спочатку deny, потім ask, потім allow. Тому правило deny не може містити виняток allowlist.
{
"permissions": {
"deny": [
"Read(.env)",
"Read(./secrets/**)",
"Bash(git push *)"
]
}
}Контролюйте вихідний трафік і перевіряйте diff
Запуск агента створює diff і набір мережевих викликів. Обидва потрібно перевірити перед злиттям або розгортанням. Перевірка безпеки diff у self-hosted середовищі виявляє інший клас змін, ніж поверхневий перегляд людиною, а матеріал про те, що coding agent надсилає за межі машини допомагає зрозуміти нормальний трафік, щоб незвичайний запит було помітно.
Що досі не вирішено
Сьогодні немає надійного розмежування між вмістом та інструкціями. Кожен доступний захист або є фільтром із певною частотою помилок, або лише обмежує наслідки. У стеку немає механізму, який позначав би фрагмент тексту як дані, яким за жодних обставин не можна підкорятися.
Фільтри допомагають, але вони також помиляються. Класифікатор, який виявляє більшість спроб ін’єкцій, усе одно має щоразу спрацювати правильно, тоді як зловмиснику достатньо мати рацію один раз. Саме ця асиметрія пояснює, чому опублікований показник успішності захисту є відправною точкою для наступної спроби, а не гарантією.
Найперспективніші дослідження зосереджені на рівні проєктування, а не моделі. CaMeL у роботі Defeating Prompt Injections by Design (Debenedetti та колеги, 2025) спочатку виокремлює потік керування й потік даних із довіреного запиту, щоб недовірені дані не могли змінити поведінку програми, а потім застосовує перевірки дозволів під час виклику інструментів. Власні показники авторів роботи на бенчмарку AgentDojo показують ціну такого підходу.
The data behind this chart
[
{
"label": "Undefended agent",
"tasks_solved_pct": 84
},
{
"label": "CaMeL",
"tasks_solved_pct": 77
}
]Агент без захисту виконав 84 відсотка завдань. CaMeL виконав 77 відсотка із гарантією безпеки. Це опубліковані в роботі показники для одного бенчмарку, а не вимірювання для вашого робочого навантаження. Різниця між ними приблизно показує, скільки сьогодні коштує реальна гарантія.
Поки такий підхід не буде реалізований в інструментах, якими ви користуєтеся щодня, виходьте з того, що агент у певний момент буде скомпрометований, і зробіть цю подію буденною. Саме для цього потрібні одноразова машина, обмежені облікові дані, контрольований вихідний трафік і звичка переглядати diff.
FAQ
Чи можна зупинити prompt injection, наказавши агенту ігнорувати інструкції у файлах?
Ні. Це речення перебуває в тому самому контекстному вікні, що й атака, і має такий самий пріоритет, як текст зловмисника. Документація Claude Code чітко розмежовує ці поняття: інструкції у вашому prompt або CLAUDE.md визначають, що агент намагатиметься зробити, але не змінюють дозволи інструмента. Розглядайте файл інструкцій як опис наміру, а все, на що ви покладаєтеся, задавайте в правилах дозволів, хуку PreToolUse або правилі firewall.
Чи є prompt injection реальною загрозою, якщо агент працює лише з моїм власним репозиторієм?
Так, оскільки ваш репозиторій містить багато тексту, написаного не вами. Файли README залежностей, URL у lockfile, тестові fixtures, vendored code і результат роботи npm install надходять під час звичайного завдання. Усе, що отримується з issue tracker або сайту з документацією, надходить так само. Ризик зростає разом з обсягом тексту, який читає агент, а корисний агент читає багато.
Чи вирішує запуск агента в контейнері цю проблему?
Він обмежує збитки, але лише якщо ви також заберете облікові дані. Контейнер із forwarded SSH agent, cloud credentials у змінних середовища та необмеженим мережевим доступом фактично передає зловмиснику майже все, що має host. Контейнер справді дає вам файлову систему, яку можна видалити, і чисте середовище для застосування правил egress. Поєднайте це з токеном, обмеженим одним репозиторієм.
Яка одна зміна найбільше зменшує ризик?
Приберіть довготривалі облікові дані зі середовища, від якого команди агента успадковують налаштування, а потім задайте для цієї машини політику egress за замовчуванням deny. Разом ці заходи усувають третю умову lethal trifecta: текст усе ще може перехопити керування агентом, але дані, до яких він дістанеться, не матимуть корисного напрямку для передачі. Запити на підтвердження та перевірка diff також допомагають, але залежать від того, чи людина зберігає пильність протягом тривалого запуску. Тому вони мають нижчий пріоритет, ніж ці дві зміни.