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

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

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

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

## Закрепляйте идентичность исполняемого файла, а не понятную человеку метку версии

Точный номер версии необходим, но сам по себе недостаточно хорошо идентифицирует локальный клиент. Метка вроде `2.4.1` говорит, что издатель планировал выпустить. Она не доказывает, какие байты вы установили, кто их подписал, какая среда выполнения их запустила и не изменило ли расширение их поведение после запуска.

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

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

Разница между *закреплением версии* и *закреплением артефакта* важна. Закрепление версии указывает установщику, какой выпуск нужно искать. Закрепление артефакта позволяет отклонить файл, отличающийся от проверенного. Команды часто смешивают эти понятия, потому что менеджеры пакетов используют слово «закрепление» для обоих случаев. В итоге они одобряют `1.4.3`, получают пересобранную `1.4.3` из скомпрометированного зеркала или изменённого кэша и не имеют локальной проверки, которая это заметит.

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

Не превращайте это в документооборот ради документооборота. Запись должна отвечать на вопрос во время инцидента: «Какой код имел право отправить этот запрос?» Если в ней сказано только «агент для программирования», ответа не будет.

## Зафиксированное дерево зависимостей не равно проверенному клиенту

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

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

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

Начните с карты того, что запускается при старте агента разработчиком. В macOS спрашивайте оболочку, а не доверяйте значку в Dock:

```sh
command -v agent-client
file "$(command -v agent-client)"
head -n 1 "$(command -v agent-client)"
```

Команда `head` важна, когда первый результат оказывается скриптом. Первая строка вроде `#!/usr/bin/env node` говорит, что среда Node и дерево пакетов входят в путь выполнения. Строка, передающая управление другому загрузчику, означает, что трассировку нужно продолжить. Не одобряйте псевдоним оболочки, символическую ссылку или стартовый скрипт так, будто это сам клиент.

Для пакета приложения проверьте настоящий исполняемый файл и метаданные подписи:

```sh
APP="/Applications/Agent Client.app"
BIN="$APP/Contents/MacOS/Agent Client"
shasum -a 256 "$BIN"
codesign -dv --verbose=4 "$APP" 2>&1 | grep -E 'Identifier=|TeamIdentifier=|Authority='
spctl --assess --type execute --verbose=4 "$APP"
```

Хеш обычно выводится в форме `digest  path`. В выводе `codesign` обычно есть идентификатор, идентификатор команды и одна или несколько строк Authority. Сохраните этот вывод вместе с записью об одобрении. `spctl` просит macOS проверить приложение по действующей политике безопасности. Это полезное свидетельство, но оно не означает, что приложению нужно дать доступ к учётным данным для развёртывания.

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

## Совместимость протокола не доказывает доверие к клиенту

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

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

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

Держите три записи отдельно:

1. Версию протокола и возможности, которые вы проверили.
2. Артефакт клиента, идентичность подписавшей стороны, среду выполнения и расширения, которые вы одобрили.
3. Разрешения сервиса, которые этот клиент может запрашивать.

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

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

## Проверяйте путь доступа, а не только окно чата

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

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

Проверяйте следующие случаи:

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

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

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

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

## Изолируйте кандидата от одобренного клиента

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

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

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

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

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

## Превратите выпуск в небольшое повторяемое изменение

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

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

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

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

```yaml
client:
  name: agent-client
  version: "2.4.1"
  executable_sha256: "replace-with-verified-digest"
  signer_team_id: "record-the-observed-team-id"
  install_path: "/Applications/Agent Client.app"
  runtime: "native bundle"
review:
  tested_on: "2025-03-08"
  reviewer: "initials"
  extensions: []
access:
  environments: ["staging", "production-read"]
  forbidden_actions: ["secret-rotation", "deployment-write"]
rollback:
  previous_version: "2.4.0"
```

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

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

## Автообновления и чувствительный доступ не должны пересекаться

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

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

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

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

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

## Подтверждение каждого действия выявляет то, чего не может выявить закрепление

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

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

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

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

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

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

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

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

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

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

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

## Закрепление ломается, если окружающая система остаётся изменяемой

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

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

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

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