Блог о безопасности FireAI

Автор FireAI Security & Research Team · Опубликовано

Проверяем подпись и нотаризацию любого приложения для Mac из терминала

Проверяем подпись и нотаризацию любого приложения для Mac из терминала

Gatekeeper выполняет эту проверку автоматически при первом запуске скачанного приложения, и чаще всего вы даже не видите деталей: появляется диалог, вы его закрываете — и всё. Эта практическая работа посвящена тому, чтобы увидеть то же, что видел Gatekeeper: какой разработчик подписал приложение, цела ли эта подпись, проверила ли её служба нотаризации Apple и что приложению разрешено делать после запуска. Все команды здесь входят в состав инструментов командной строки Xcode (xcode-select --install, если их ещё нет), и все они предназначены только для чтения — вы изучаете приложение, а не меняете его.

Сама подпись: codesign -dvvv

codesign — это инструмент Apple для создания и проверки подписей кода. Флаг -d показывает информацию о подписанном коде по указанному пути, а согласно man-странице, «увеличение уровня многословности даёт больше вывода» — так что -dvvv (display, три уровня verbose) даёт полную картину одной командой:

Терминал — полная информация о подписи
codesign -dvvv /Applications/Example.app
# example output, trimmed to the fields that matter
Executable=/Applications/Example.app/Contents/MacOS/Example
Identifier=com.example.app
Format=app bundle with Mach-O universal (x86_64 arm64)
CodeDirectory v=20500 size=... flags=0x10000(runtime) hashes=...
Signature size=4741
Authority=Developer ID Application: Example Software LLC (ABCDE12345)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Team Identifier=ABCDE12345
Runtime Version=14.0.0

Четыре поля, на которые стоит смотреть каждый раз. Authority — это цепочка сертификатов: у обычного стороннего приложения она должна заканчиваться на Apple Root CA через Developer ID Certification Authority, а верхняя строка называет разработчика. Team Identifier — это десятизначный идентификатор аккаунта разработчика в Apple: именно это значение стоит сравнивать между приложениями, которые, как вы считаете, принадлежат одной компании, поскольку оно не меняется между их релизами. flags=0x10000(runtime) означает, что включён hardened runtime — набор дополнительных ограничений (например, защита от внедрения кода в процесс), который Apple требует для нотаризации. А полное отсутствие строки Authority — просто Signature=adhoc — означает, что приложение не подписано либо подписано самостоятельно (self-signed) без какой-либо цепочки к Apple.

Совпадает ли подпись с файлами до сих пор: --verify --deep --strict

Подпись — это обещание относительно конкретного набора байтов на момент подписания. --verify проверяет, выполняется ли это обещание до сих пор — согласно man-странице, она подтверждает, «что код по этим путям подписан, что подпись действительна и что все запечатанные компоненты не изменены». Для бандла приложения — каталога, полного вложенных ресурсов, фреймворков и вспомогательных исполняемых файлов, а не единого файла — важны два дополнительных флага:

Терминал — глубокая, строгая проверка
codesign --verify --deep --strict --verbose=2 /Applications/Example.app
/Applications/Example.app: valid on disk
/Applications/Example.app: satisfies its Designated Requirement

--deep важен потому, что, согласно man-странице, проверка вложенного содержимого по умолчанию «ограничена поверхностным исследованием, которое может не выявить изменения во вложенном коде» — глубокий режим рекурсивно проверяет каждый встроенный фреймворк и вспомогательный инструмент, а не только внешний бандл. --strict добавляет дополнительные проверки, которые Apple считает достаточно важными, чтобы не включать их по умолчанию, включая требование, чтобы любая символическая ссылка внутри бандла «указывала на запечатанные файлы внутри своего бандла», отклоняя ссылку, указывающую за пределы приложения или на что-то незапечатанное — известный приём для того, чтобы протащить неподписанную полезную нагрузку внутрь в остальном легитимно подписанного бандла. Если любая из проверок не проходит, вы увидите code failed to satisfy specified code requirement или примечание, называющее именно тот вложенный элемент, который не совпадает с тем, что было изначально запечатано — прочитайте эту строку, она называет файл.

Читаем entitlements

Entitlements — это конкретные права, которые подпись предоставляет приложению: доступ к камере, возможность обращаться к сети вне песочницы, отключение проверки библиотек и так далее. codesign -d --entitlements - извлекает их; согласно man-странице, «встроенные данные entitlements будут аналогично извлечены и записаны» по указанному пути, а - означает стандартный вывод:

Терминал — заявленные entitlements приложения
codesign -d --entitlements - /Applications/Example.app
# example output, trimmed
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>

Большинство entitlements ничем не примечательны и соответствуют очевидным потребностям приложения — видеозвонки, запрашивающие доступ к камере, не повод для беспокойства. Стоит обратить внимание на disable-library-validation: это значит, что приложение будет загружать код извне собственного подписанного бандла, что нормально для некоторых приложений с плагинами, но представляет собой более широкую дверь, чем нужна большинству приложений. Это деталь, которую стоит отметить, а не автоматический тревожный сигнал, и именно такую деталь невозможно увидеть, не задав вопрос.

Действительно ли служба нотаризации Apple это проверила: spctl и stapler

Действительная подпись доказывает лишь то, что приложение не изменялось с момента, когда его подписал разработчик — она ничего не говорит о том, смотрела ли на него Apple. Это и добавляет нотаризация. Собственная документация Apple описывает автоматическую службу нотаризации как сканирующую «ваше программное обеспечение на предмет опасных компонентов, проверяющую проблемы с подписью кода и быстро возвращающую вам результаты». Когда проверка пройдена, «служба нотаризации создаёт билет (ticket), который нужно прикрепить (staple) к вашему программному обеспечению; служба нотаризации также публикует этот билет онлайн, чтобы Gatekeeper мог его найти».

spctl --assess — практический способ спросить вердикт у собственного движка политик Gatekeeper, а не выводить его самостоятельно. Согласно man-странице, --assess «выполняет оценку указанных файлов», а -v / --verbose, повторяемый для большей детализации, описывается просто как запрос «более подробного вывода»:

Терминал — собственная оценка Gatekeeper
spctl --assess -vv /Applications/Example.app
/Applications/Example.app: accepted
source=Notarized Developer ID

source=Notarized Developer ID — результат, который вы хотите увидеть: он означает, что Gatekeeper обнаружил действительную подпись Developer ID и билет нотаризации, независимо от того, прикреплён ли этот билет к приложению или найден онлайн. Результат source=Unnotarized Developer ID означает, что приложение подписано, но Apple его не нотаризовала (пока), а простое rejected означает, что Gatekeeper заблокировал бы его запуск в конфигурации по умолчанию.

Чтобы проверить именно прикреплённый билет, xcrun stapler validate ищет билет, физически прикреплённый к приложению, а не запрашивает его онлайн у Gatekeeper — это полезно, чтобы убедиться, что приложение по-прежнему пройдёт проверку вообще без интернет-соединения:

Терминал — проверка прикреплённого билета
xcrun stapler validate /Applications/Example.app
Processing: /Applications/Example.app
The validate action worked!

Флаг карантина: откуда берётся проверка при первом запуске

Руководство Apple по безопасности платформы объясняет, что «Gatekeeper также отслеживает происхождение файлов, записанных скачанным программным обеспечением», и «запрашивает одобрение пользователя перед первым открытием скачанного программного обеспечения». Механизм, стоящий за этим, — расширенный атрибут com.apple.quarantine, который Safari, Mail и другие приложения прикрепляют ко всему, что сохраняют из сети. xattr -l выводит его список, и, согласно man-странице, эта опция «выводит как имена атрибутов, так и соответствующие значения»:

Терминал — проверяем, помечен ли файл как скачанный
xattr -l ~/Downloads/Example.dmg
com.apple.quarantine: 0081;65e1a2b3;Safari;

Полное отсутствие вывода означает, что у файла нет флага карантина — либо он был создан локально, либо попал на диск способом, который не устанавливает этот атрибут (некоторые архиваторы и менеджеры пакетов его пропускают), либо флаг был удалён вручную командой xattr -d com.apple.quarantine. Последний случай стоит знать отдельно: это документированный способ полностью обойти проверку Gatekeeper при первом запуске, применяемый к файлам, которым доверяют или думают, что доверяют, и к нему стоит относиться осознанно, а не копировать команды из незнакомой инструкции в терминале.

Разбор на примере: два приложения, утверждающие, что они от одного разработчика

Конкретный способ применить всё это вместе: допустим, у вас есть две копии приложения, обе якобы от одной компании — одна с сайта разработчика, другая по ссылке, которую вам кто-то прислал. Запустите codesign -dvvv на обеих и сравните строку Team Identifier — это десятисимвольная строка, привязанная к конкретному аккаунту Apple Developer, и, в отличие от отображаемого имени или идентификатора бандла, её нельзя просто так воспроизвести без доступа к сертификату подписи этого аккаунта. Если две копии показывают разные Team Identifier, вы имеете дело не с двумя сборками одного приложения, а с двумя разными подписантами, один из которых — не тот, за кого выдаёт себя скачанный файл. Затем выполните spctl --assess -vv на подозрительной копии — несовпадающий или отсутствующий источник нотаризации у копии, которая утверждает, что идентична нотаризованному оригиналу, — это уже подтверждение, а не просто намёк.

Читаем картину целиком: реальные тревожные признаки

  • Полное отсутствие цепочки Authority или цепочка, не заканчивающаяся на Apple Root CA — за приложением не стоит подотчётная идентичность разработчика.
  • codesign --verify завершается неудачей, особенно с сообщением, называющим конкретный вложенный файл — что-то внутри бандла изменилось после подписания.
  • spctl --assess возвращает rejected, либо Team Identifier в codesign -dvvv не совпадает с разработчиком, которого вы ожидаете для этого продукта.
  • Флаг карантина явно удалён у файла, который вы не скачивали сами, либо файл пришёл необычным путём (скрипт, вложение письма, переименованное так, чтобы выглядеть чем-то другим).
  • Entitlements широко открыты — полный доступ к диску, отключённая проверка библиотек, неограниченный доступ к сети — у приложения, заявленное назначение которого явно этого не требует.

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

Какое место здесь занимают FireAI и HisnLabs

A clean signature and a stapled ticket say an app hasn’t been tampered with since Apple checked it — they say nothing about what it connects to afterward, which is the question FireAI’s per-app rules and on-device review are built to keep answering, signature by signature, connection by connection.

FireAI — это собственный продукт HisnLabs: ИИ-файрвол, работающий прямо на вашем Mac. Он показывает простым языком каждое соединение, которое устанавливают ваши приложения, и позволяет вам решать, что покидает ваш Mac, — его ИИ работает локально, поэтому ваш трафик никогда не отправляется ни нам, ни кому-либо ещё. Команда исследователей безопасности HisnLabs — это те, кто поддерживает точность этих решений: они каталогизируют, какие домены являются обычной телеметрией, а какие — реальным сервисом, отслеживают страну и сеть, стоящие за соединением, и обучают локальную модель (функцию Autopilot) на реальных шаблонах трафика — и ничего из этого не покидает ваш Mac.

Вы можете прочитать о технических решениях, лежащих в его основе, или попробовать FireAI в течение 17 дней на странице FireAI от HisnLabs.

Источники