# Как безопасно отозвать доступ AI-агента во время выполнения задачи

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

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

## Отзыв доступа должен остановить будущие полномочия, не уничтожив место происшествия

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

Перед изменением настроек управления разделите четыре понятия:

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

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

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

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

## Составьте карту действий до первого инцидента

Нельзя отозвать то, что невозможно определить. У каждого запуска агента должен быть идентификатор, который встречается в записи локального процесса, журналах инструментов и запросах, доходящих до удаленных систем. Достаточно случайного идентификатора запуска, если передавать его везде одинаково. Не используйте имя человека или расплывчатую метку вроде `coding-agent`: оба варианта смешивают несвязанные действия в одну категорию.

Для каждого маршрута, по которому агент может выполнять действия, зафиксируйте ответы на пять практических вопросов:

1. Какой локальный процесс владеет маршрутом и как его остановить?
2. Какая сессия или какой токен представляет этот процесс шлюзу действий?
3. Какие удаленные операции могут продолжиться после отзыва локального доступа?
4. Где независимые журналы сервисов записывают эти операции?
5. Кто может отключить маршрут, если его обычный владелец недоступен?

Это не бюрократия ради бюрократии. Во время инцидента оператору не придется выяснять, запускается ли миграция базы через дочерний процесс оболочки, задание CI, HTTP-запрос или отдельный облачный процесс.

Если API это позволяет, передавайте идентификатор запуска в заголовке запроса. Для агента с идентификатором запуска `run_2025_04_18_7f3c` оболочка может добавить заголовок корреляции, не помещая секрет в запрос или окружение агента:

```sh
export AGENT_RUN_ID="run_2025_04_18_7f3c"
curl -sS -X POST "$ACTION_ENDPOINT" \
  -H "X-Agent-Run-ID: $AGENT_RUN_ID" \
  -H "Content-Type: application/json" \
  --data @request.json
```

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

```text
time=2025-04-18T14:12:09Z request_id=req_91a run_id=run_2025_04_18_7f3c caller=agent-gateway method=POST path=/deployments status=202
```

Статус `202` важен. Он означает, что сервис принял работу, но не подтверждает ее завершение. Именно здесь часто ошибаются при разборе инцидентов: считают последний вывод инструмента агента результатом, хотя удаленная система только подтвердила постановку задания в очередь.

Для SSH укажите удаленную учетную запись, целевые хосты, принудительные команды, если они есть, и место, где записываются входы и выполненные команды. Локальная запись терминала полезна, но не доказывает, что команда не выполнилась удаленно. На этот вопрос отвечают собственные журналы хоста и соответствующий журнал аудита сервиса.

## Остановите активный запуск так, чтобы сохранить контроль

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

1. Запишите в журнал инцидента идентификатор запуска, идентификатор локального процесса, оператора, замеченное поведение и текущее время. Скопируйте текущую расшифровку работы агента и последний видимый вызов инструмента до закрытия интерфейса.
2. Отзовите или заблокируйте активную сессию на шлюзе, который разрешает действия. Если есть безопасная операция только для чтения, проверьте, что новый тестовый вызов от того же процесса получает отказ.
3. Если операционная система позволяет, сначала приостановите локальный процесс, а не завершайте его сразу. Приостановка дает стабильное дерево процессов и список открытых файлов. Завершайте процесс после сбора этих данных, если только продолжение работы не создает немедленную угрозу.
4. Найдите и остановите дочерние процессы, фоновые задания, контейнеры и удаленные сессии, связанные с этим запуском.
5. Отмените принятую удаленную работу с помощью собственного механизма отмены сервиса, затем сохраните ответ и статус.

В macOS или Linux начните с проверки, а не с бездумной команды `kill -9`. Замените пример идентификатора процесса на записанный вами:

```sh
ps -o pid,ppid,pgid,lstart,command -p 48192
pgrep -P 48192 -a
lsof -nP -p 48192
kill -STOP 48192
```

Первая команда записывает родительский процесс и время запуска. `pgrep` показывает прямых дочерних процессов, а `lsof` часто обнаруживает открытые файлы рабочей области, сокеты и каналы, которые стоит сохранить. `kill -STOP` замораживает процесс, не давая ему выполнить еще одну команду очистки. Он не замораживает уже отделившийся дочерний процесс, удаленное задание или API-запрос, который пересек сетевую границу.

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

Если задача выполняет удаленную работу, используйте идентификатор операции удаленной системы. Например, API может вернуть это при принятии запроса:

```json
{
  "operation_id": "op_4e2b7c",
  "status": "queued"
}
```

Сохраните ответ, затем вызовите документированную конечную точку отмены или воспользуйтесь консолью сервиса под контролем человека. Запишите результат отмены. Ответ `cancel_requested` означает, что нужно продолжать опрашивать операцию, пока она не перейдет в состояние `cancelled`, `completed` или другое конечное состояние. Не пишите в заметках «остановлено» только потому, что локальный процесс исчез.

## Сохраните доказательства до сброса рабочей области

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

По возможности сохраните исходные временные метки и соберите следующие материалы:

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

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

```sh
mkdir -p incident-run_2025_04_18/evidence
cp agent-transcript.txt incident-run_2025_04_18/evidence/
ps -o pid,ppid,pgid,lstart,command -p 48192 > incident-run_2025_04_18/evidence/process.txt
shasum -a 256 incident-run_2025_04_18/evidence/* > incident-run_2025_04_18/SHA256SUMS.txt
```

Манифест результатов должен выглядеть так:

```text
8f7c...  incident-run_2025_04_18/evidence/agent-transcript.txt
30b1...  incident-run_2025_04_18/evidence/process.txt
```

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

В руководстве NIST по обработке инцидентов компьютерной безопасности SP 800-61 сдерживание, устранение, восстановление и работа после инцидента рассматриваются как отдельные действия. Для инцидентов с агентами это разделение особенно уместно. Команды часто сразу переходят к устранению, удаляя рабочий каталог или меняя токен. Потом выясняется, что в удаленной расшифровке был точный текст запроса, необходимый для поиска удаленного ресурса.

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

## Завершение родительского процесса не доказывает, что работа остановлена

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

Представьте агента для программирования, который отправляет запрос на развертывание, запускает по SSH команду с фоновой миграцией, а затем ждет тестов. Оператор видит неожиданную цель в продакшене и останавливает родительский процесс агента. Сервис развертывания уже принял запрос, а удаленная оболочка запустила команду через `nohup`. Оба действия продолжаются. Последняя строка терминала может сообщать лишь о закрытии соединения.

Проверка должна включать три разных шага:

### Проверьте работу, принятую API

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

Не полагайтесь только на HTTP-статус. `200` может описывать синхронное изменение, документ состояния или ответ промежуточного компонента. `202` обычно означает, что обработка принята, но окончательный смысл определяет документация сервиса. Изучите ее до создания сценария отмены для этого сервиса.

### Проверьте работу, отделившуюся от агента

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

Проверьте и обратные вызовы. Сгенерированный скрипт мог настроить вебхук, создать процесс для запроса на слияние или запустить CI. Это отдельные исполнители со своими учетными данными и расписаниями. Отзыв доступа исходного агента не отзывает их доступ.

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

## Меняйте учетные данные только при наличии оснований считать их раскрытыми

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

Эти условия оправдывают немедленное отключение или замену:

- Агент получил секрет в открытом виде в запросе, переменной окружения, конфигурационном файле, командной строке или выводе инструмента.
- Учетные данные попали в расшифровку, diff репозитория, временный файл или журнал CI, доступный за пределами команды инцидента.
- Нельзя проверить дочерний процесс, удаленную сессию или исходящее соединение, которое могло прочитать секрет.
- Учетные данные дают широкие полномочия и не имеют надежных журналов каждого использования.
- Поставщик сообщает, что учетные данные использовались из неизвестной личности или местоположения.

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

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

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

## Запись инцидента должна выдерживать скептическую проверку

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

Составьте хронологию с явными оценками уверенности. Например, «агент предложил запрос» следует из расшифровки. «Шлюз выполнил запрос» подтверждается записью действия на шлюзе. «Поставщик принял операцию» следует из записи аудита поставщика. «Ресурс изменился» подтверждается итоговым состоянием ресурса и историей изменений. Это разные утверждения, и для каждого нужны свои доказательства.

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

Sallyport записывает сессии агентов и отдельные действия в одном зашифрованном журнале аудита с цепочкой хешей, а `sp audit verify` проверяет цепочку офлайн без ключа хранилища. Это дает проверяющему простой тест для экспортированной копии журнала:

```sh
sp audit verify /path/to/exported-audit-log
```

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

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

## Возобновляйте разработку с новой границей, а не с повторным открытием сессии

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

Создайте новый запуск с новым идентификатором сессии. Перед выдачей новой сессии доступа к учетным данным или целям в продакшене проверьте diff репозитория и созданные файлы. Если предыдущую задачу нужно продолжить, передайте новому запуску короткую письменную сводку: какие удаленные действия завершились, какие остаются отмененными и какие ресурсы нужно проверить.

Для продолженной задачи используйте более узкий набор разрешений. Задаче по проверке кода редко нужен доступ к развертыванию. Задаче, которая должна читать данные из продакшена, обычно не нужен доступ на запись. Речь не о том, чтобы заставить каждого разработчика изучать сложный язык политик. Нужно лишь не переносить вчерашние широкие полномочия в запуск, начавшийся после инцидента.

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

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

## Сделайте отзыв доступа обычным действием оператора

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

Самый сильный вариант архитектуры прост: разделить полномочия. Агенты могут планировать действия и запрашивать их. Локальная контрольная точка хранит учетные данные, определяет процесс, который делает запрос, записывает запрос и может отказать этому процессу, не передавая ему секрет. Когда оператор нажимает кнопку остановки, следующий привилегированный вызов должен завершиться отказом, а предыдущие вызовы должны оставаться доступными для проверки.

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