# Может ли аудиторский след подтверждений доказать, кто разрешил действие агента?

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

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

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

## Событие подтверждения должно отвечать не только «да»

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

В момент принятия решения зафиксируйте:

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

Не записывайте `user=alex` только потому, что на компьютере есть учетная запись с именем Alex. Это может быть лучшая доступная атрибуция, и ее все равно стоит сохранить, но называйте ее правильно: контекст локальной учетной записи. Если биометрическая проверка прошла, запишите, что событие авторизовала биометрия, зарегистрированная на этом устройстве. Такие формулировки точнее, чем «кто-то по имени Alex подтвердил действие», потому что не приписывают журналу знания, которых у него нет.

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

Распространенная ошибка - записывать подтверждение как атрибут конечного действия, например `approved=true`. Так событие превращается в ярлык. Вы теряете время между запросом и решением, источник решения, область согласия и последующий отзыв. Кроме того, становится невозможно отличить подтверждение пользователя от разрешения по умолчанию, кэшированного разрешения или правила автоматизации.

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

## Пять идентичностей сохраняют честность временной ленты

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

Первая - **процесс агента**. Записывайте идентификатор процесса или запуска, исполняемый файл или центр сертификации кода, запустивший процесс, время начала и время завершения. Человек должен иметь возможность ответить: «Какая работающая программа запросила это?» Названия проекта или расшифровки чата недостаточно. Одновременно могут работать две копии одного агента программирования, причем одна может быть безопасной, а другая направленной на другой репозиторий.

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

Третья - **контекст подтверждающего**. Он включает учетную запись устройства, аутентифицированного пользователя приложения, если он есть, и способ подтверждения. Не заставляйте поле подтверждающего содержать факты, которые оно не может подтвердить. `local_account=maya`, `method=touch_id` и `device_id=...` понятны. `human=maya` заявляет больше. В одной среде такое утверждение может быть оправданным, а в другой общий компьютер или разблокированный рабочий стол сразу делает его сомнительным.

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

Пятая - **выполненная операция**. Для HTTP это разрешенное назначение, метод запроса, нормализованный путь, выбранные несекретные сведения о запросе, статус ответа и время выполнения. Для SSH это идентификатор хоста, удаленная учетная запись, команда или хеш разрешенной команды, код завершения и время выполнения. Событие должно показывать, что запустилось на самом деле, а не только что было запрошено.

Эти идентичности образуют граф, а не плоскую строку:

```text
agent_process
  -> session
    -> approval_decision
      -> credential_use
        -> executed_call
```

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

## Нажатие и Touch ID дают разные доказательства

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

Используйте явное поле метода с ограниченным набором значений. Например:

```json
{
  "approval_id": "apr_01J8K4VY5Q",
  "occurred_at": "2026-07-22T14:18:06.184Z",
  "decision": "approved",
  "method": "touch_id",
  "approver": {
    "local_account": "maya",
    "identity_assurance": "device_account_and_biometric"
  },
  "scope": "credential_use",
  "session_id": "ses_01J8K4TE0M",
  "requested_call_id": "call_01J8K4VPM2"
}
```

Точные названия полей не принципиальны. Важно разделение. `method` показывает, каким способом завершилось подтверждение. `identity_assurance` описывает, что система вправе сказать о человеке. `scope` показывает, что именно разрешило решение. `requested_call_id` связывает подтверждение с запросом, который существовал до появления окна перед человеком.

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

Обратная ошибка не менее опасна: считать Touch ID декоративным элементом. Если для действия требовалось биометрическое подтверждение, а журнал сводит его к `approved=true`, запись уже не показывает, что более строгий контроль действительно сработал. При проверке теряется возможность отличить намеренное подтверждение от случайного нажатия на общее окно авторизации сессии.

Неудачные попытки биометрической проверки тоже нужно записывать разумно. Обычно аудиту важно знать, что запрошенное действие не получило разрешения, но редко требуется фиксировать каждую ошибку аутентификации на уровне операционной системы. Полезным событием будет `decision=denied_or_cancelled`, `method=touch_id` и причина вроде `user_cancelled`, если платформа позволяет ее определить. Не превращайте шлюз действий в сборщик биометрической телеметрии.

## Авторизация сессии и подтверждение каждого вызова имеют разную область действия

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

Представьте процесс агента, запущенный в 09:00. Шлюз показывает карточку авторизации с указанием центра сертификации кода процесса. Разработчик нажимает «Подтвердить». В 09:20 агент выполняет HTTP-запрос с учетными данными, для которых не требуется подтверждение каждого использования. Вызов может быть разрешен, потому что сессия все еще авторизована. Правильная временная лента показывает два отдельных факта:

1. В 09:00 разработчик разрешил сессию `ses_...` на время работы этого процесса.
2. В 09:20 эта сессия использовала учетные данные `cred_...` для выполнения вызова `call_...`.

Не нужно придумывать подтверждение пользователя в 09:20. Разработчик не видел и не подтверждал именно этот вызов. Его покрывало более раннее разрешение.

Теперь изменим одну настройку: учетные данные требуют подтверждения при каждом использовании. В 09:20 шлюз снова запрашивает решение, и разработчик подтверждает его через Touch ID. Новое событие должно указывать на `call_...`, содержать `scope=credential_use` и `method=touch_id`. Авторизация сессии остается важной, потому что объясняет, почему агент смог обратиться к учетным данным. Но она не заменяет второе решение.

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

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

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

## Записи об учетных данных должны показывать полномочие, но не раскрывать его

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

Дайте каждому сохраненному секрету непрозрачный неизменяемый идентификатор, например `cred_01J8K...`. Добавьте понятную человеку метку, например `payments-readonly` или `staging-deploy`. Запишите канал и способ подстановки, например `http_bearer`, `http_custom_header` или `ssh_key`. Так расследующий специалист получит достаточно контекста, чтобы задать правильный вопрос, но ключ не окажется внутри записи.

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

```json
{
  "credential_use_id": "use_01J8K4WHD7",
  "occurred_at": "2026-07-22T14:18:06.221Z",
  "credential": {
    "id": "cred_01J7ZB7F8P",
    "label": "inventory-production",
    "channel": "http",
    "injection": "bearer"
  },
  "session_id": "ses_01J8K4TE0M",
  "approval_id": "apr_01J8K4VY5Q",
  "call_id": "call_01J8K4VPM2",
  "secret_exposed_to_agent": false
}
```

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

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

Для SSH не считайте псевдоним хоста полной идентичностью назначения. `prod-db` удобно читать, но псевдонимы могут измениться. Записывайте настроенное назначение и подтверждение идентичности хоста, которое проверяет ваше соединение. Если агент запросил `prod-db`, а разрешенное назначение оказалось другим, это различие должно попасть в запись выполнения. Именно такие детали имеют значение после неудачного развертывания.

## Выполненный вызов доказывает, что действие произошло

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

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

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

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

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

Используйте разные типы событий. `call.requested`, `call.dispatched`, `call.completed` и `call.failed_before_dispatch` сообщают больше, чем перегруженное событие `call` со статусом, смысл которого меняется. Дополнительные записи позволяют ответить, произошел ли сетевой тайм-аут после подстановки учетных данных, заблокировала ли запрос локальная проверка и получил ли удаленный сервис ответ.

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

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

Представьте агента развертывания, который получает подтверждение сессии в 10:02. Он читает репозиторий, готовит выпуск, а в 10:17 вызывает конечную точку развертывания в рабочей среде. Конечная точка принимает запрос. В 10:18 разработчик замечает, что выбрана неправильная среда.

Слабый журнал выглядит так:

```text
10:02 approved agent
10:17 deployment API call succeeded
```

Такой журнал почти ни на что не отвечает. Был ли вызов в 10:17 покрыт подтверждением в 10:02? Требовали ли учетные данные второго запроса? Какой процесс сделал вызов? Использовал ли агент нужные учетные данные для развертывания или более широкий токен? Отправила ли система запрос в рабочую среду или он попал туда из-за перенаправления либо ошибки конфигурации? Нажал ли человек кнопку, использовал Touch ID или вообще не видел окна для конкретного действия?

Полезная временная лента выглядит иначе:

```text
10:02:11  session.opened       ses_71  process=proc_44 signer=known_authority
10:02:14  approval.approved    apr_02  method=click scope=session session=ses_71 account=maya
10:17:03  call.requested       call_88 POST deploy.example/release target=production session=ses_71
10:17:04  credential.selected  use_53  credential=cred_prod_deploy call=call_88
10:17:04  call.dispatched      call_88 destination=deploy.example
10:17:06  call.completed       call_88 status=202 remote_request=req_914
```

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

Если добавить подтверждение каждого вызова, правильной дополнительной записью будет не еще одна строка «подтверждено». Она должна указывать разрешенный вызов и его область действия:

```text
10:17:04  approval.approved    apr_03  method=touch_id scope=credential_use
          session=ses_71 call=call_88 credential=cred_prod_deploy account=maya
```

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

## Целостность аудита требует отдельного утверждения

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

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

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

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

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

## Стройте временную ленту вокруг связей и проверяйте неприятные случаи

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

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

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

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

Язык интерфейса должен быть таким же точным, как модель данных. «Сессия подтверждена нажатием» понятно. «Учетные данные для развертывания в рабочей среде подтверждены через Touch ID для этого вызова» тоже понятно. «Подтверждено» - декоративное слово статуса, которое заставляет читателя самостоятельно придумать самые важные детали.

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