Сохраняется ли одобрение сессии при замене процесса через exec?
Одобрение сессии при замене процесса требует чёткой границы: сохраняйте доверие только для того же проверенного образа и снова запрашивайте его для нового исполняемого файла.

Одобрение сессии должно следовать за проверенной идентичностью запуска, а не за идентификатором процесса в операционной системе. Когда процесс вызывает exec, ядро может сохранить его PID, заменив код, аргументы, окружение и зачастую практическую задачу процесса. Если старое одобрение переживает такую замену, непроверенный код получает полномочия, которые человек предоставил совсем другой программе.
Правило, которое я бы заложил в продукт, простое: если замена создаёт другую идентичность исполняемого файла, сначала запросите новое одобрение, и только потом разрешайте действия с учётными данными. Сохраняйте непрерывность лишь при узко определённом повторном запуске того же исполняемого образа после проверки. В легитимном сценарии это иногда добавит лишний запрос. Зато закроет гораздо более опасную лазейку в сценариях, где агент, оболочка, обновлятор или скомпрометированная зависимость могут сами выбрать, что запустится дальше.
Одобрение связывается с исполнителем, а не с PID
PID указывает на служебную ячейку учёта ядра, а одобрение должно указывать на программу, которая может воспользоваться решением человека. Это разные задачи. Проблемы начинаются сразу, как только одна программа запускает другую, если считать их одним и тем же объектом.
Sallyport описывает авторизацию на уровне сессии как одобрение нового процесса агента, действующее до завершения этого запуска. Это понятное правило для пользователя, но внутри системы нужно дать ему более точное определение: одобренный запуск должен оставаться тем же одобренным исполняемым файлом, а не просто сохранять тот же PID.
Это различие важно, потому что секреты остаются в шлюзе, а агент получает результаты, но не сами учётные данные. Такая архитектура устраняет знакомую катастрофу, когда дочерний процесс читает токен из переменной окружения. Но она не лишает подменённый процесс возможности попросить шлюз вызвать API или выполнить SSH с сохранёнными учётными данными. Если такой процесс унаследует одобрение, он сможет причинить тот же вред внешним системам, ни разу не увидев секрет.
Явно определите объект одобрения. В него должны входить стабильная идентичность исполняемого файла, событие создания процесса или одноразовый идентификатор сессии, информация о подписывающей стороне, показанная человеку, а также достаточный контекст запуска, чтобы объяснить назначение процесса. Используйте PID как атрибут аудита и удобный ориентир при поиске неполадок. Не превращайте его в носитель полномочий.
Такое различие помогает честно отзывать доступ. Если пользователь отзывает запуск, шлюз должен отклонять последующие вызовы этого запуска, даже если тот повторно запускает себя через exec. Если замена получает новое одобрение, для неё нужно создать новую запись сессии, которую пользователь сможет отозвать отдельно.
Exec меняет исполняемый файл, даже если PID остаётся прежним
exec должен сбрасывать авторизацию при загрузке другой программы, потому что он заменяет программный образ, хотя операционная система может сохранить PID. В POSIX сказано, что функция exec «заменяет образ текущего процесса». Формулировка короткая, но важная часть в ней верна: непрерывность относится к административному учёту, а не доказывает, что исполнителем остаётся тот же субъект.
На примере оболочки это легко не заметить. Разработчик запускает одобренного агента, агент вызывает помощник, а помощник использует exec, чтобы не оставлять лишний родительский процесс. В списке процессов до и после замены может отображаться один и тот же PID. Если поиск сессии отвечает «pid 4127 одобрен», новый помощник получает полномочия старого агента.
Замена исполняемого файла может изменить риск так, как PID описать не способен. Новая программа может обращаться к другому хосту, обрабатывать файл из непроверенного репозитория, загружать расширения, принимать данные через стандартный ввод или существовать только для передачи запросов на выполнение действий. В номере процесса ничего этого нет.
Не принимайте решение по тому, выглядит ли новая командная строка знакомо. Аргументы команды дают полезный контекст, но программа может их переписать, а оболочка может запустить цель, не связанную с безобидной на вид командой. Определяйте идентичность исполняемого файла по образу, который фактически запустила операционная система, а наблюдаемые путь и аргументы сохраняйте как сведения для человека, проверяющего запрос.
posix_spawn нужно рассматривать в том же контексте, но результат у него другой. Он создаёт дочерний процесс, а не заменяет образ вызывающей программы. По умолчанию дочерний процесс не должен получать одобрение сессии. Связка fork и exec должна приводить к тому же результату: новый процесс запрашивает одобрение для образа, который в итоге запускается.
Новому исполняемому файлу нужно новое одобрение
Шлюз должен требовать нового одобрения, прежде чем другая программа сможет выполнить какое-либо действие через уже авторизованную сессию. Определяйте «другую программу» по проверенной идентичности образа, а не по имени файла и не по отображаемому названию.
Практическая запись идентичности может содержать неизменяемый идентификатор исполняемого файла, назначенный платформой, хеш подписанного кода, если он доступен, подписывающую сторону и конкретный путь к исполняемому файлу, зафиксированный при запуске. Первые два поля отвечают на вопрос, изменился ли образ. Подписывающая сторона показывает проверяющему, кто отвечает за код. Путь показывает, откуда произошёл запуск. Каждое поле отвечает на свой вопрос, поэтому не сводите их к одной строке.
Именно этот сценарий заставляет считать разрешительное наследование удобным, пока не становится поздно. Одобренный агент для программирования вызывает помощник репозитория. Помощник видит настройку окружения и заменяет себя локально собранной утилитой по тому же ожидаемому пути. Утилите не нужно извлекать API-ключ. Она просит выполнить HTTP-действие, которое удаляет развёртывание, меняет настройку оплаты или публикует выпуск. Шлюз видит старый PID и принимает вызов. Человек одобрил агента для программирования, а не утилиту, которая выбрала себя после одобрения.
Обычно унаследованное одобрение оправдывают усталостью от запросов. Это реальная проблема, но разрешение произвольных замен её не решает. Оно скрывает решение от единственного человека, который может оценить, уместна ли замена. Сократите число карточек, сделав обычный запуск агента стабильным, а новый запрос показывайте в момент смены исполнителя.
Проверка должна выполняться до внедрения учётных данных, запуска SSH и любого другого исходящего действия. Она также должна выполняться до того, как шлюз вернёт метаданные, которые помогут замене подготовить действие. Ожидающее одобрение не означает частично авторизованное состояние.
Повторный запуск того же образа, узкое исключение
Проверенный повторный запуск точно того же исполняемого образа может сохранить одобрение сессии, потому что новый исполнитель не появляется. Программы используют повторный запуск самих себя для чистого перезапуска, изменения дескрипторов файлов или намеренной передачи управления после обновления собственного окружения. Новая карточка в таком случае добавит шум, не добавив содержательного решения.
Исключение должно оставаться узким. Шлюз должен сравнивать текущий наблюдаемый образ с образом, которому было выдано одобрение. Если идентичность полностью совпадает, можно сохранить одноразовый идентификатор сессии и записать событие непрерывности в журнал аудита. Если шлюз не может установить эту идентичность, нужно снова запросить одобрение. Неопределённость не доказывает безопасность замены.
Не превращайте это в исключение для всей подписывающей стороны. Один издатель может выпускать агента, установщик, диагностический инструмент и сетевую утилиту. У этих программ могут быть совершенно разные полномочия на действия. Видимая подписывающая сторона помогает пользователю оценить запрос, но не должна молча распространять одобрение на каждый бинарный файл с той же подписью.
Не делайте исключение и для всего пути. Самообновление может заменить байты по фиксированному пути. После одобрения символическая ссылка может указывать в другое место. Скрипт может сохранить имя файла, изменив содержимое. Сравнение идентичностей должно учитывать все три случая.
Когда легитимное обновление устанавливает новую версию агента, дайте ей запросить одобрение заново. Такой запрос сообщает важный факт: программа, которая будет выполнять действия, изменилась. Пользователь, ожидавший обновление, одобрит его одним нажатием. Тот, кто не ожидал изменений, сможет остановить процесс.
Подписывающая сторона помогает оценить программу, но не расширяет права
Показывайте подписывающую сторону заметно, потому что она отвечает на реальный вопрос пользователя: кто выпустил этот исполняемый файл? Но не принимайте этот ответ за полноценное правило авторизации.
Модель подписи кода Apple даёт macOS возможность идентифицировать подписанный код и проверять его целостность в рамках действующих правил доверия. Это весомое свидетельство для карточки одобрения. Но оно не означает, что весь код одного издателя выполняет одну операционную задачу, и не сообщает шлюзу, выбрал ли родительский процесс этот исполняемый файл через настройку непроверенного репозитория.
Хорошая карточка одобрения сначала показывает имя и путь к исполняемому файлу, затем подписывающую сторону, идентичность родительского процесса и причину нового запроса. При замене через exec она должна одним предложением описать обе стороны изменения: одобренный агент заменил себя этой программой. Человеку не придётся восстанавливать переход, сравнивая две разрозненные карточки.
Для подписанных и неподписанных случаев нужна одна и та же граница. Неподписанная локальная сборка разработчика может быть ожидаемой частью процесса разработки, а подписанная утилита всё равно может оказаться неподходящим процессом для наследования полномочий. Карточка должна описывать имеющиеся свидетельства, не создавая впечатления, будто подпись превращает новую программу в старую.
Избегайте расплывчатых ярлыков вроде «доверенный процесс». Они подталкивают людей одобрять категорию вместо конкретного запроса. Называйте исполняемый файл и показывайте его связь с уже одобренным процессом. Так проверяющему будет что узнать или отклонить.
Скрипты и лаунчеры показывают слабое место границы
Для запуска скрипта нужны две идентичности: интерпретатор, который его выполняет, и сам скрипт, содержимое которого управляет работой. Если проверять только интерпретатор, любой скрипт оболочки будет выглядеть как одна и та же оболочка. Если проверять только скрипт, можно не заметить интерпретатор, выбранный через строку shebang или оболочку.
Для скрипта оболочки записывайте идентичность исполняемого файла интерпретатора, разрешённый путь к скрипту и хеш его содержимого. Если скрипт выполняется через одобренную оболочку, а затем оболочка запускает через exec другой бинарный файл, замена бинарного файла всё равно требует нового одобрения. Скрипт не должен превращаться в туннель через границу сессии.
Лаунчеры создают похожую проблему. Одобренный лаунчер может проверить файл конфигурации, найти инструмент в PATH, скачать помощник или выбрать каталог версии. Часто лаунчер одобряют, считая выбранную цель частью того же запуска. Это неверно, если лаунчер принимает решение, важное для безопасности, уже после одобрения пользователя.
Используйте один из двух подходов. Если лаунчер знает цель до первого вызова шлюза, пусть карточка показывает конечную цель и одобряет идентичность этого запуска. Если выбор происходит позже, пусть лаунчер работает без права на действия и запрашивает одобрение, когда выбранная программа впервые попытается выполнить действие. Второй вариант оставляет более честный след в аудите.
То же правило относится к подключаемым модулям среды выполнения и встроенным интерпретаторам. Нативный хост может не измениться, пока загружает код из каталога проекта. Если этот код способен формировать запросы к шлюзу, одной идентичности образа хоста недостаточно для описания фактического исполнителя. Шлюз не может безопасно проверять каждую среду выполнения, поэтому безопаснее ограничить одобрение сессии определённым исполняемым файлом агента и требовать нового решения, когда он передаёт управление действиями внешней программе.
Дочерний процесс и замена через exec требуют разного делегирования
Дочерний процесс не должен наследовать одобрение сессии только потому, что оно есть у родителя. Создание процесса добавляет нового исполнителя, а exec заменяет текущего исполнителя. В обоих случаях при смене программы, выполняющей вызовы шлюза, нужно новое одобрение, но связи в аудите будут разными.
Для дочернего процесса создайте нового кандидата на сессию со ссылкой на родительскую сессию. Покажите родителя на карточке одобрения как полезный контекст, а не как источник разрешения. Если пользователь одобрит дочерний процесс, выдайте ему собственный одноразовый идентификатор сессии и собственный механизм отзыва.
При exec закройте или замените старую идентичность исполняемого файла и создайте кандидата на замену, связанного с предыдущей сессией. Если новый образ в точности совпадает с одобренным, сохраните непрерывность и запишите результат. Если он отличается, остановитесь у шлюза и дождитесь решения. Так одна огромная запись сессии не будет смешивать несколько несвязанных программ.
Не создавайте общий токен делегирования, который родитель сможет передавать дочерним процессам или заменам. Токен со смыслом «всё, что я запускаю, может действовать» станет лёгкой добычей для скомпрометированного агента или запутавшейся оболочки. Ссылки на родителя в журнале достаточно, чтобы восстановить историю, не превращая родство в полномочие.
Такое разделение улучшает и расследование инцидентов. Можно выяснить, запустил ли родитель дочерний процесс, заменил ли он себя и одобрил ли человек конечный исполняемый файл. Плоская запись разрешённых запросов не ответит на эти вопросы после вредоносного действия.
Записывайте замену отдельным событием
Журнал аудита должен показывать замену через exec как отдельное событие, независимо от того, сохранил ли шлюз одобрение или запросил его заново. Без этого события проверяющий увидит действия одной сессии и может решить, что все они выполнены одной стабильной программой.
Контракт системы может выглядеть так. Это пример сведений, которые нужно сохранять, а не обязательный формат обмена:
{
"event": "execution_replaced",
"session_id": "sess_8f2c",
"previous_image": {
"identity": "image:4f19...",
"path": "/work/agent/bin/agent"
},
"current_image": {
"identity": "image:b66a...",
"path": "/work/agent/bin/release-helper",
"signing_authority": "Example Development Team"
},
"decision": "approval_required",
"parent_relation": "exec"
}
В событии нужны предыдущая и текущая идентичности, а не только отметка о том, что произошёл exec. В нём также должен быть результат решения. Последующая запись действия должна ссылаться на текущую идентичность сессии, чтобы расследующий мог связать действие с одобрением, которое его разрешило.
Храните эту запись в режиме добавления вместе с остальной историей действий. Зашифрованный журнал Sallyport с цепочкой хешей и автономная проверка sp audit verify особенно полезны здесь: событие замены и последующие действия могут входить в одну проверяемую последовательность. Результат проверки показывает, что сохранённая последовательность не менялась. Он не делает чрезмерно широкую политику одобрения приемлемой.
При отзыве сессии записывайте отзыв для той идентичности исполняемого файла, которому принадлежала сессия. Если замена получила новое одобрение, она должна оставаться видимой отдельно. Это не позволит элементу управления отзывом создавать впечатление более широкого охвата, чем есть на самом деле.
На карточке одобрения нужны факты до и после замены
Карточка нового одобрения после exec должна с одного взгляда объяснять замену и помогать легко принять безопасное решение. Общие запросы приучают людей нажимать кнопку, не вчитываясь. Карточка с именем изменившейся программы даёт повод остановиться только тогда, когда действительно что-то изменилось.
Сначала покажите идентичность новой программы и канал действий, которым она хочет воспользоваться. Затем укажите, что её запустил уже одобренный процесс, покажите имя и путь старой программы, новый путь и подписывающую сторону. Если замена произошла через скрипт или лаунчер, опишите эту связь простыми словами.
Используйте модель решения, в которой ясно указано, что именно одобряет пользователь. Одобрение замены должно разрешать работу этой программы до её завершения с учётом шлюза хранилища и любых учётных данных, для которых требуется подтверждение при каждом вызове. Оно не должно задним числом разрешать соседние процессы, будущие замены или обновлённый файл по тому же пути.
Есть простой тест для формулировки: сможет ли уставший разработчик отличить ожидаемый помощник от неожиданного загрузчика? Если нет, в карточке не хватает важных фактов. Дополнительные декоративные формулировки о безопасности этого не исправят.
Сокращайте число запросов стабильностью запуска, а не широким наследованием. Обычный агент, который сохраняет один образ, должен показывать одну карточку сессии. Процесс, сменивший исполнителя, должен показать карточку, потому что пересёк границу, ради которой и существует понятие сессии.
Стройте тесты вокруг реальных способов обхода
Проверяйте границу авторизации заменами, которые сохраняют внешне знакомые идентификаторы. Тесты стабильного бинарного файла по успешному сценарию не обнаружат ошибки, из-за которых полномочия достаются не той программе.
Начните с таких случаев:
- Одобренный бинарный файл повторно запускает точную копию самого себя через exec. Шлюз сохраняет сессию и записывает событие непрерывности.
- Бинарный файл повторно запускает через exec другой подписанный исполняемый файл от той же подписывающей стороны. Шлюз блокирует действия, пока человек не одобрит новый запуск.
- Символическая ссылка сохраняет ту же строку пути, но указывает на другой исполняемый файл. Шлюз снова запрашивает одобрение.
- Содержимое скрипта оболочки меняется после начала сессии. Следующий запрос, управляемый скриптом, не использует прежнее решение.
- Лаунчер запускает дочерний процесс, а тот пытается выполнить HTTP- или SSH-действие. Дочернему процессу нужно отдельное решение для сессии.
Затем проверьте порядок операций. Пусть замена сразу после exec отправит запрос на действие. Убедитесь, что шлюз отклоняет его или удерживает до решения, и что до получения результата одобрения не начинается ни внедрение учётных данных, ни выполнение SSH. Так можно обнаружить реализации, которые обновляют журнал уже после отправки работы.
Проверьте отзыв в том же наборе тестов. Отзовите одобренный запуск, попробуйте повторно запустить тот же образ через exec и убедитесь, что состояние отзыва имеет приоритет. Одобрите новую замену, отзовите только исходную сессию и проверьте, что обе записи ведут себя согласно заявленным элементам управления продукта. Модель сессий вызывает доверие, когда её пограничные случаи соответствуют видимым пользователю правилам.
Наконец, изучите вывод аудита глазами человека. Должна быть возможность проследить одно действие через событие замены до карточки одобрения, которая охватывала исполняемый файл. Если для этого приходится сопоставлять PID, угадывать по времени или доверять пути, который мог измениться, в модели всё ещё есть брешь.
Именно в момент замены процесса нужно проявить строгость. У старого кода было решение. Новому коду нужно своё.
Вопросы и ответы
Сохраняет ли exec тот же идентификатор процесса для целей одобрения?
Нет. exec обычно сохраняет идентификатор процесса в операционной системе, но заменяет программный образ, который теперь представляет этот идентификатор. Если считать PID идентификатором одобрения, другая программа получит полномочия, которые человек ей не предоставлял.
Нужно ли запрашивать новое одобрение при вызове exec?
Требуйте новое одобрение всякий раз, когда меняется идентификатор исполняемого файла. Узко определённый повторный запуск точно того же проверенного образа может сохранить сессию, но одного совпадения пути или подписывающей стороны недостаточно.
Достаточно ли пути к исполняемому файлу, чтобы сохранить сессию?
Нет. Путь указывает местоположение, но не доказывает, что именно будет запущено. Обновлятор, замена символической ссылки, подменённый файл или изменившийся интерпретатор могут оставить путь прежним, изменив код, который получает полномочия на действия с учётными данными.
Достаточно ли информации о подписывающей стороне, чтобы доверять заменённому исполняемому файлу?
Она помогает человеку понять, кто выпустил программу, и должна отображаться на карточке одобрения. Но она не доказывает, что всем исполняемым файлам этого издателя нужны одинаковые права, и не показывает, какой скрипт, аргументы или родительский процесс запустили программу.
Как одобрение сессии должно работать со скриптами оболочки?
Считайте интерпретатор исполняемым образом и показывайте идентификатор и хеш скрипта как дополнительный контекст. Если скрипт изменился, считайте запрос новым, даже если бинарный файл интерпретатора остался прежним.
Чем отличаются дочерний процесс и замена через exec?
Это разные решения. Дочерний процесс запускается как новый процесс и должен запрашивать авторизацию под собственным идентификатором. Замена через exec меняет образ внутри существующего процесса, поэтому при изменении образа авторизацию нужно сбросить.
Что должен записать журнал аудита, когда процесс запускает другую программу через exec?
Записывайте предыдущий образ, заменяющий образ, связь с родительским процессом, время, решение об одобрении и унаследованный идентификатор сессии, если он есть. Из журнала должно быть очевидно, сохранил ли шлюз непрерывность или снова запросил решение пользователя.
Может ли одобренный лаунчер передать свою сессию другой программе?
Не делайте одобрение передаваемым только потому, что запускающая программа подписана или уже одобрена. Либо одобрите конечный исполняемый файл, который будет выполнять действия, либо потребуйте нового одобрения после того, как лаунчер найдёт и запустит эту программу.
Какие случаи с exec нужно проверить перед выпуском?
Проверьте замену через символическую ссылку, изменённый скрипт, другой интерпретатор, обновление на месте и лаунчер, который выбирает помощник во время работы. Также убедитесь, что отклонённая замена не может использовать защищённый канал до завершения нового одобрения.
Что должна показывать карточка нового одобрения после exec?
Покажите прежние и новые имена исполняемых файлов, пути, подписывающие стороны и причину, по которой шлюз считает запрос новым. Человеку легче быстро принять правильное решение, когда карточка ясно описывает изменение, а не показывает общий запрос доверия.