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

Подтверждение локального агента должно относиться к тому исполняемому файлу, который вы проверили, к тому процессу, который вы разрешили, и к тому доступу, который он запросил в конкретный момент. Если исполняемый файл меняется, старое разрешение на практике перестает действовать, даже если имя файла, идентификатор пакета и значок остались прежними.
Это становится очевидным, когда приходится разбирать разрешение, выданное версии 1.8, которым незаметно воспользовалась версия 1.9. Большинство ошибок начинается с чрезмерно широких проверок идентичности: пути, названия продукта или знакомого Team ID подписи. Они могут быть частью доказательств, но ни одно из них само по себе не должно переносить привилегированное доверие через изменение кода.
Доверие относится к наблюдаемому исполняемому файлу
Локальный агент не равен личности издателя. Это конкретная подписанная программа, загруженная по определенному пути, с конкретным родительским процессом, аргументами, окружением и набором запрошенных действий. Издатель может выпускать много программ. Одна программа может сильно измениться между релизами. Стабильное имя почти ничего не говорит ни о первом, ни о втором.
Это особенно важно, когда агент может вызывать платные API, использовать SSH-учетные данные, изменять репозиторий или отправлять данные за пределы компьютера. Разрешение не является комплиментом поставщику. Это разрешение процессу выполнять действия с реальными последствиями.
Разделяйте следующие идентичности:
- Идентичность файла определяется подписанным кодом и его дайджестом на диске.
- Идентичность подписи показывает, какую сторону macOS указывает как подписавшую этот код.
- Происхождение издателя подтверждает, что файл прошел через ожидаемый вами путь выпуска.
- Идентичность среды выполнения включает процесс, который запустил агент, и доступ, которым он просит воспользоваться.
Команды часто сводят все четыре пункта к фразе «это наш агент». Так замена по тому же пути получает доступ, которого она не заслужила. Простое правило безопасности: замена исполняемого файла запускает новое решение о доверии.
Хеш обнаруживает замену, но не показывает, кто подписал новую версию. Подпись указывает на подписавшую сторону, но не доказывает, по какому каналу файл был загружен. Чистая загрузка не говорит, нужен ли новой версии более широкий доступ. Нужны все три проверки, потому что каждая выявляет свой тип проблемы.
Не впадайте и в противоположную крайность, навсегда закрепляя каждый байт. Сборки законно меняются, сертификаты обновляются, а релизы должны получать обновления. Речь не о неизменности. Важно, чтобы кто-то увидел изменение, установил, что именно изменилось, и явно решил, по-прежнему ли оправдан запрошенный уровень полномочий.
Подпись кода отвечает на более узкий вопрос, чем кажется
Подпись кода macOS позволяет операционной системе проверить, что подписанный код не изменился после того, как подписавшая сторона создала подпись. Для распространения через Developer ID Gatekeeper также оценивает идентичность разработчика и сам объект. Это полезные свидетельства, но не полный вердикт о цепочке поставок.
В технической заметке Apple TN2206 «macOS Code Signing In Depth» идентичность кода отделена от более широких условий, при которых система принимает код. Особенно важна часть о designated requirements: macOS может использовать такое требование, чтобы признать будущую версию принадлежащей той же идентичности кода. Эта непрерывность помогает обычным обновлениям приложений. Но ее недостаточно как единственного условия для агента, который может тратить деньги или обращаться к закрытой инфраструктуре.
Действительная подпись не отвечает на следующие вопросы:
- Хотел ли издатель, чтобы именно этот релиз попал на ваш компьютер?
- Не была ли подпись создана с помощью скомпрометированной учетной записи издателя?
- Добавила ли новая версия возможность, которая меняет уровень риска?
- Не заменили ли установщик, программа обновления или скрипт запуска один компонент, оставив неизменной проверенную вами часть?
Gatekeeper и нотариальная проверка снижают вероятность запуска macOS очевидно ненадежного ПО. Но они не превращают корректно подписанного агента в одобренного обладателя ваших рабочих полномочий. Прием кода операционной системой и разрешение привилегированных действий должны оставаться разными решениями.
То же относится к продлению сертификата. Продленный сертификат может быть обычным административным событием, но он все равно меняет свидетельства, на которые вы опираетесь. Если Team ID, идентификатор и ожидаемый путь издателя остались прежними, оператор может одобрить обновление после проверки. Если подписывающая сторона перешла к другой организации, остановитесь и найдите понятное объяснение издателя. Не принимайте неожиданную смену идентичности только потому, что приложение запускается без предупреждения.
Происхождение издателя не совпадает с подписью
Происхождение показывает, как бинарный файл попал к вам и соответствует ли этот путь обычной практике выпуска издателя. Подпись файла, скопированного из неизвестного вложения в чате, говорит, кто подписал эту копию. Она не объясняет, почему вы получили ее именно там.
Для рабочего агента сохраняйте небольшую квитанцию релиза при установке или обновлении. Это может быть текстовый файл в репозитории, который управляет агентом, запись об изменении или строка во внутреннем журнале релизов. В квитанции укажите версию, источник установки, обнаруженную подписывающую сторону, Team ID, дайджест, дату проверки и имя человека, который принял версию. Если издатель публикует дайджест релиза, сохраните и его.
Канал релиза стоит проверить здравым смыслом. Пришла ли программа обновления из ожидаемого приложения? Опубликовал ли издатель примечания к этой версии? Совпадают ли имя архива, подпись пакета и путь назначения с обычным способом установки? Неожиданное обновление через новый канал заслуживает такого же подозрения, как неожиданная подписывающая сторона.
Не путайте общедоступный репозиторий исходного кода с артефактом релиза. Репозиторий может показывать историю исходников, тогда как конвейер релиза собирает другой бинарный файл. И наоборот, подписанный бинарный файл может быть настоящим, даже если его сборку невозможно воспроизвести. Это разные уровни уверенности. Указывайте, какой уровень у вас есть, вместо того чтобы выдавать одно доказательство за другое.
Сравнение дайджестов помогает, когда издатель предоставляет аутентифицированную контрольную сумму. Оно бесполезно, если контрольная сумма и бинарный файл скопированы с одной и той же ненадежной страницы. Полезное сравнение опирается на независимую запись релиза под управлением издателя, запись доверенного менеджера пакетов или заранее проверенный внутренний источник.
Запрошенный доступ нужно проверять вместе с обновлением
Бинарное обновление может сохранить ту же подписывающую сторону и все равно требовать меньшего доступа, чем старая версия. Причина проверки не сводится к страху перед вредоносным кодом. Новое поведение способно сделать старое разрешение неоправданным.
Разберитесь, какие внешние действия процесс будет выполнять после обновления. Раньше агент мог читать метаданные задач, а теперь создавать запросы на изменение. Раньше он использовал одноразовый тестовый токен, а теперь может выполнять SSH-команды на общем сервере. Новый плагин, изменившийся формат конфигурации или другая команда по умолчанию могут расширить реальные возможности процесса, не запрашивая у операционной системы новое разрешение.
Проверка доступа должна охватывать рественную границу действий:
- К каким HTTP-хостам, областям учетных записей и записям с учетными данными обратится процесс?
- К каким SSH-направлениям и удаленным командам он получит доступ?
- Из какой рабочей директории, с какими хуками репозитория, аргументами и переменными окружения он запускается?
- Получает ли он теперь ввод из другого источника, например из комментария к запросу на изменение или журнала сборки?
- Может ли он запускать другой локальный исполняемый файл, которого не было в предыдущей проверке?
Именно здесь широкое постоянное разрешение дает сбой. Решение «разрешить этому агенту» скрывает главное: что именно ему разрешено делать, с чьими учетными данными и в ответ на какой ввод?
Для процессов-агентов происхождение входных данных требует такого же внимания. Обновление, после которого агент программирования начинает действовать на основе недоверенного текста задачи, может превратить обычный доступ к репозиторию в путь для prompt injection. Подпись кода не проверяет инструкции, которые получает процесс. Она охватывает только программу, интерпретирующую их.
Делайте экраны подтверждения и внутренние записи конкретными. Указывайте полномочия процесса, назначение, класс учетных данных и то, меняет ли действие удаленное состояние. Оператор не сможет принять взвешенное решение по уведомлению «Агент запрашивает доступ».
Завершайте старую сессию до того, как начнет действовать новый код
Самое понятное правило заключается в том, чтобы связывать разрешение с одним работающим процессом и завершать его при выходе процесса. Замененный бинарный файл создает новый процесс, поэтому получает новое разрешение. Это избавляет от хрупких попыток решить, какое обновление достаточно мало, чтобы унаследовать прежнюю выдачу.
Не позволяйте процессу обновлять себя на месте и продолжать пользоваться авторизацией, полученной до замены. Некоторые программы обновления делают именно это: старый процесс скачивает архив, записывает новый исполняемый файл поверх старого пути, а затем запускает помощника или выполняет себя заново. Если слой авторизации проверяет только путь или долгоживущую запись клиента, новый код продолжает работать по старому решению.
Практическая запись решения может выглядеть так:
process path: /Users/dev/tools/agent/bin/agent
observed digest: 8a4b...e19c
identifier: dev.example.agent
TeamIdentifier: A1B2C3D4E5
parent: interactive shell in approved repository
requested actions: issue API read, test-host SSH command
approval scope: this process only
expires: process exit
Не превращайте дайджест в постоянную запись списка разрешений. Сохраняйте его, чтобы видеть, что следующее подтверждение относится к другому коду. Когда процесс перезапускается после обновления, сравните новое наблюдение с предыдущим и покажите оператору важные различия.
Проверка может потребоваться и после перезапуска той же версии, если изменился контекст запуска. Бинарный файл, запущенный интерактивной оболочкой в проверенном репозитории, не равен тому же бинарному файлу, который без участия человека запускает задание сборки с более широким окружением. Исполняемый файл составляет лишь часть объекта проверки. Контекст процесса завершает картину.
Проверяйте свидетельства macOS перед подтверждением замены
Перед первым привилегированным вызовом можно проверить кандидат на замену встроенными инструментами macOS. Запускайте команды для того исполняемого файла, который будет запущен, а не для похожей копии в Downloads и не для внешнего пакета приложения, внутри которого, как вам кажется, он находится.
codesign -d -vvv /Users/dev/tools/agent/bin/agent 2>&1
spctl -a -t exec -vv /Users/dev/tools/agent/bin/agent
shasum -a 256 /Users/dev/tools/agent/bin/agent
Обычно вывод codesign содержит поля примерно такого вида:
Identifier=dev.example.agent
Authority=Developer ID Application: Example Publisher (A1B2C3D4E5)
TeamIdentifier=A1B2C3D4E5
CDHash=8a4b9c0d...
spctl сообщает результат проверки и обычно показывает источник для принятого ПО Developer ID. shasum выводит полный дайджест SHA-256, за которым следует путь к файлу. Сохраните важный вывод вместе с квитанцией релиза. Точная формулировка зависит от версии macOS, поэтому сравнивайте поля идентичности и результат проверки, а не пробелы или порядок полей.
Для приложения в пакете проверяйте и вложенный код. Подписанный внешний пакет может содержать помощников, фреймворки или инструменты командной строки. Если агент напрямую запускает помощника, проверяйте именно этого помощника. Файл, запрашивающий доступ, и есть тот файл, чью идентичность нужно связать с решением.
Успешная проверка codesign не означает, что исполняемый файл прошел нотариальную проверку или подходит для ваших задач. Она означает, что подпись соответствует правилам проверки этой команды. Это различие стоит сохранять в записях об инцидентах. Иначе кто-то позже прочитает «подпись действительна» как «релиз проверен и одобрен», хотя это гораздо более сильное утверждение.
Стабильный путь позволяет легко разрешить не тот код
Представьте локальную оболочку /Users/dev/bin/agent. Разработчик однажды разрешает ее, потому что она обращается к API проекта только для чтения. Позже оболочка запускает автоматическое обновление, скачивает нового помощника и сохраняет тот же путь. Компонент разрешений узнает путь и выдает новому помощнику старое разрешение.
Для этого не нужен злоумышленник. Релиз может быть настоящим. Проблема в том, что запись разрешения говорит «путь совпадает с разрешенным», хотя на самом деле решение принималось для более старой программы с более узким поведением.
Ситуация еще хуже, если сама оболочка является скриптом. Скрипты оболочки часто запускают версии бинарных файлов через стабильную символическую ссылку, читают конфигурацию из доступной для записи директории или выбирают помощника из PATH. Проверка подписи оболочки мало что говорит о конечном исполняемом файле, если после проверки оболочка может перенаправить запуск.
Исправьте порядок действий. Определите конечный исполняемый файл. Проверьте его подпись и дайджест. Зафиксируйте родительский процесс и аргументы. Затем авторизуйте работающий процесс. Если средство запуска может после подтверждения заменить или выбрать другой исполняемый файл, привяжите шлюз действий к процессу, который он наблюдает в момент подключения, и отклоняйте несовпадение.
Популярный вариант заключается в автоматическом наследовании разрешения для любого обновления, подписанного тем же Team ID. Команды выбирают его, потому что запросы раздражают разработчиков, а сертификаты релиза обычно остаются стабильными. Для привилегированных агентов это неверный подход: он превращает учетные данные издателя в безусловное разрешение на любое будущее поведение. Снижайте усталость от запросов с помощью разрешений на время сессии, узкого доступа и понятных уведомлений об обновлениях, а не делайте обновления невидимыми.
Изменения издателя требуют отдельной записи о переходе
Изменение подписывающей стороны может быть законным. Компании приобретают продукты, переходят на другую учетную запись Developer ID или заменяют старый процесс распространения. Относитесь к таким событиям как к миграциям, а не к обычным обновлениям.
Требуйте отдельную запись с прежней идентичностью, новой идентичностью, версией, в которой произошло изменение, и доказательствами, на основании которых вы его приняли. Хорошие доказательства включают подписанное объявление в привычном канале релизов издателя, соответствующие примечания к выпуску в ожидаемом репозитории и пакет, полученный обычным способом. Одно необъяснимое всплывающее окно доказательством не является.
Изменение Team ID по умолчанию должно запрещать запуск, пока кто-то не проверит миграцию. Новый сертификат при том же Team ID можно принять после обычной проверки происхождения и доступа. Изменившийся дайджест при том же сертификате также требует нового подтверждения, потому что это новый код, даже если остальные поля совпадают.
Не создавайте широкое исключение вроде «принимать все будущие идентичности для этого имени приложения». Оно превращает разовую миграцию в постоянную уязвимость. Сохраняйте новую идентичность только после того, как проверяющий примет конкретный релиз. Следующее изменение должно снова запустить ту же проверку.
Если ваша команда распространяет внутренние агенты, публикуйте ожидаемую идентичность подписи, дайджест релиза и порядок обновления там, где операторы смогут найти их без помощи самого агента. Агент не может убедительно подтвердить собственную замену, пока эта замена является объектом проверки.
Небольшой протокол лучше длинного списка исключений
Чтобы решения об обновлениях были предсказуемыми, не нужен сложный механизм правил. Нужен короткий протокол, который применяется при каждом изменении исполняемого файла.
- Остановите старый процесс и отзовите его активную сессию.
- Определите конечный исполняемый файл, который будет подключаться или выполнять действия.
- Сравните его дайджест, идентификатор, подписывающую сторону и Team ID с предыдущей квитанцией релиза.
- Проверьте ожидаемый канал выпуска и запишите причину любого изменения идентичности.
- Проверьте запрошенные назначения и учетные данные, затем подтвердите новую сессию процесса или отклоните ее.
Этот протокол отделяет обычное техническое обновление от настоящей аномалии. Новый дайджест при той же подписывающей стороне и обычном происхождении релиза означает событие для проверки, а не чрезвычайную ситуацию. Незнакомая подписывающая сторона, неожиданный установщик и запрос рабочего SSH-ключа должны остановить выпуск до расследования.
Авторизация сессий Sallyport помогает сделать границу процесса видимой: система распознает новый процесс агента до разрешения его запуска, а настройка подтверждения каждого вызова позволяет принимать более строгие решения для важных учетных данных. Это не отменяет необходимости проверять обновленного агента, но не дает старому запуску незаметно передать разрешение следующему.
Запись должна быть достаточно короткой, чтобы люди ее поддерживали. Полезные сведения таковы: наблюдаемый файл, его подписавшая сторона, источник, контекст запуска и одобренные действия. Если релиз меняет что-либо из этого, человек, отвечающий за риск, должен увидеть изменение до того, как агент начнет действовать.
Аудит должен различать действующего субъекта и издателя
Запись аудита, в которой сказано только «агент использовал учетные данные», сильно усложняет разбор инцидента. Фиксируйте, какой процесс отправил запрос, какую подписывающую сторону указала macOS, какая сессия его авторизовала и какое действие действительно произошло. Идентичность издателя объясняет, кто подписал код. Запись о процессе показывает, кто действовал.
Это различие важно, когда запуск агента заканчивается проблемой. Нужно понять, изменила ли новая версия поведение, запускался ли старый бинарный файл из неожиданного места, подтвердил ли человек другой запрос на доступ, чем ему казалось, или недоверенный ввод направил ожидаемый процесс в нежелательную сторону. Одно поле издателя на эти вопросы не отвечает.
Отделяйте целостность от понятности записи. Защищенная от незаметного изменения запись может показать, менялись ли прежние строки. Но она не восстановит отсутствующие факты после события. Сохраняйте дайджест исполняемого файла и контекст решения в момент подтверждения, пока их еще можно наблюдать.
Sallyport строит журналы Sessions и Activity из одного зашифрованного аудита с цепочкой хешей, а sp audit verify может автономно проверить эту цепочку поверх шифротекста. Используйте такие свидетельства для проверки записи, а затем связывайте их с квитанцией релиза, объясняющей, почему исполняемый файл вообще получил доступ.
Первое изменение, которое стоит внести, это удалить все правила разрешений, сопоставляющие только путь, отображаемое имя или издателя. Замените их разрешением для текущего процесса, завершайте его при выходе и требуйте нового решения после замены бинарного файла. Эта граница выявляет путь обновления, который широкие списки разрешений обычно пропускают.
Вопросы и ответы
Достаточно ли того же Team ID, чтобы доверять обновленному агенту?
Нет. Стабильный Team ID или сертификат разработчика показывает, что подписавшая сторона по-прежнему связана с новым файлом. Но это не доказывает, что новый код заслуживает доступа старого процесса или что релиз пришел по ожидаемому каналу издателя.
Нужно ли заново подтверждать агента после каждого обновления?
Считайте новый исполняемый файл новым объектом проверки. Завершите сессию старого процесса, изучите подпись и происхождение новой версии, затем запросите новое подтверждение до того, как она получит учетные данные или начнет внешние действия.
Что на самом деле доказывает подпись кода macOS?
Подпись связывает личность подписавшей стороны с содержимым файла и позволяет macOS обнаружить изменения после подписания. Она не доказывает, что учетная запись издателя не была взломана, что установщик получен из правильного источника или что запрошенный программой доступ по-прежнему оправдан.
Что такое CDHash и стоит ли его сохранять?
CDHash идентифицирует каталог подписанного кода, который использует macOS, и меняется при изменении значимой части подписанного кода. Он помогает обнаружить, что исполняемый файл отличается от проверенной сборки, но не заменяет проверку подписавшей стороны и источника релиза.
Что делать, если после обновления у агента другой сертификат подписи?
Не переносите старое разрешение молча. Изменение подписавшей стороны требует отдельной проверки, а изменение Team ID обычно должно блокировать запуск, пока кто-то не подтвердит документированный переход продукта к другому издателю или намеренную миграцию.
Когда нужно проверять исполняемый файл агента в macOS?
Проверьте исполняемый файл после его появления на диске и до первого привилегированного действия. Путь запуска важен: программа обновления, архиватор или скрипт замены позднее могут поместить в то же место другой файл.
Как проверить происхождение локального исполняемого файла агента?
Проверьте обычный канал релизов издателя, подписанный установщик или архив, опубликованную контрольную сумму и примечания к версии, объясняющие изменения. Если источник файла установить нельзя, одна лишь действительная подпись недостаточно убедительна, чтобы дать ему доступ к рабочим учетным данным.
Может ли подтверждение сессии пережить обновление агента на месте?
Она должна заканчиваться при завершении процесса и не переходить к заменившему его исполняемому файлу. Запущенный процесс, который меняет себя, требует особого внимания: объект, получивший разрешение, уже не совпадает с кодом, который будет выполнять последующие вызовы.
Безопасно ли разрешать агенту доступ по пути к файлу?
Список разрешений может уменьшить число запросов, но он ненадежен, если проверяет только путь, имя пакета или Team ID. Сопоставляйте конкретный наблюдаемый исполняемый файл текущего запуска и запрашивайте новое решение при изменении хеша, требования к подписи, контекста запуска или запрошенного доступа.
Как Sallyport обрабатывает доверие после обновления агента?
Да, если система хранит секрет вне процесса агента и связывает разрешение с текущим процессом. Sallyport делает это через шлюз хранилища и авторизацию сессии, но оператор все равно должен считать изменившийся исполняемый файл новым запуском, который нужно проверить.