# 首次启动前验证下载的 macOS 应用

安全应用天生会获得不寻常的访问权限。它可能读取网络流量、添加系统扩展、安装特权辅助程序、检查文件，或请求屏幕录制和辅助功能权限。把它的首次启动当作普通应用安装来处理，是一个本可避免的错误。

合适的审核不需要对整个产品进行逆向工程。你需要确认自己拿到的是预期的文件，签名中的发布者身份与预期一致，内容仍与已签名版本相符，并且 macOS 会在正常保护机制下接受它。在应用获得权限、凭据或管理员密码之前完成这些检查。

这套审核流程刻意带有重复性，这正是它的优点。根据 macOS 恰好显示的警告临时操作一次，很容易被绕过。每次引入新的安全应用都记录同一组简短检查，更难被欺骗，也更容易交给另一位工程师。

## 文件名不是发布者

名为 `Acme Security.app` 的文件几乎不能说明任何问题。复制的图标、熟悉的产品名称和制作精美的下载页面都很容易仿造。你需要确认的是签名中记录的身份，然后将它与从发布者处通过其他渠道获得的信息进行比较。

从源头开始。优先使用发布者自己的发布页面，或发布者在文档中指定的发布位置。如果能够获取原始版本，就不要使用下载聚合站、文件镜像或聊天线程中转发的链接。如果文件是同事发来的，询问他们从哪里获得文件，不要把“文件在同事手里”当作来源证明。

这里有两个不同的问题：

- 这个文件是否来自我原本打算使用的地方？
- 为这个可执行文件签名的发布者，是否就是我原本打算信任的发布者？

人们经常把这两个问题合并成一个。这样一来，被入侵的供应商下载页面仍可能通过粗略审核；一个拥有有效 Apple Developer ID 的应用，也可能仅仅因为名称像某个知名供应商，就获得信任。

检查文件前，先建立预期。记录应用的产品名称、版本、预期文件类型、发布者名称，以及发布者公布的 Team Identifier 或签名证书信息。如果供应商从未公布稳定的标识符，就结合多个独立信号：其文档、源代码仓库、你已经信任的早期版本，以及来自已知联系人的支持回复。没有此前将证书与供应商联系起来的理由时，单独看证书主题行是不够的。

对于声称要保护其他软件的应用，我希望发布者能让这些信息容易获取。如果供应商要求你关闭 Gatekeeper、运行将 curl 输出直接传入 shell 的命令，或在没有明确解释的情况下忽略不匹配，它已经没有通过安装审核。

## 打开前验证交付文件

在把应用拖到 Applications 或运行安装程序之前，先检查下载的容器。容器决定哪些命令有用，以及接下来可能发生什么。

`.dmg` 是磁盘映像。挂载它会显示其中的内容，但本身不应启动其中的应用。`.pkg` 是安装程序包，在你授权 Installer 后可以修改系统。`.zip` 是压缩包，通常会展开成应用包或另一个容器。单独的 `.app` 已经是应用包。

审核结束前，不要改动原始下载文件。Finder 通常会自动展开 ZIP，这很方便，却也更容易让人忘记供应商发布的哈希对应的究竟是哪一个文件。如果发布者为 ZIP 提供 SHA-256 校验和，就在解压前验证 ZIP。如果校验和对应磁盘映像，就在挂载前验证磁盘映像。

在终端中使用完整引用的路径。从 Finder 将文件拖入终端，可以安全地插入该路径。

```sh
shasum -a 256 \"$HOME/Downloads/VendorSecurity.dmg\"
```

输出大致如下：

```text
9fd1...e84c  /Users/you/Downloads/VendorSecurity.dmg
```

将全部 64 个十六进制字符与发布者提供的值比较。不要只比较开头几个字符就认为检查完成。

只有当预期摘要是通过不同于下载文件的渠道获得时，校验和才具有有力的证明作用。最简单的可靠安排是：从供应商发布页面获取安装程序，再从签名的发布说明、源代码仓库的发布信息或单独维护的安全页面获取校验和。紧挨下载按钮显示的哈希仍能发现意外损坏，但如果攻击者同时控制页面和文件，它无法保护你。

如果供应商没有发布校验和，不要假装自己已经确定文件安全。继续进行签名和公证检查，并提高对发布者验证的重视。如果应用对你的环境影响重大，可以要求供应商提供签名摘要或发布清单。这是合理的要求。

对于磁盘映像，还可以让 macOS 验证其内部结构：

```sh
hdiutil verify \"$HOME/Downloads/VendorSecurity.dmg\"
```

验证成功说明映像结构和校验块在内部保持一致。它不能识别发布者，也不能替代供应商提供的 SHA-256 摘要。

## 有效签名证明完整性，但不能替你做判断

代码签名回答了一个范围有限但很有用的问题：签名者批准之后，已签名的包是否发生过变化？它还会显示签名链和 Team Identifier。但它不能告诉你应用设计是否良好、发布者是否值得信任，或应用请求的访问权限是否合理。

挂载磁盘映像或展开压缩包后，直接检查应用包。下面的命令会验证代码签名，并检查包内嵌套的已签名代码：

```sh
codesign --verify --deep --strict --verbose=4 \"/Volumes/Vendor Security/Vendor Security.app\"
```

成功时通常几乎没有输出。终端的退出状态就是结果，因此命令执行后安静地返回提示符是正常的。失败时，`codesign` 会指出发生变化的文件、无效签名，或无法通过验证的嵌套组件。

然后输出签名详情：

```sh
codesign -dvvv \"/Volumes/Vendor Security/Vendor Security.app\" 2\u003e\u00261 | \\
  egrep \"^(Identifier|TeamIdentifier|Authority|Timestamp)=\"
```

输出大致如下：

```text
Identifier=com.vendor.security
TeamIdentifier=ABCDE12345
Authority=Developer ID Application: Vendor, Inc. (ABCDE12345)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=Jan 16, 2026 at 14:32:09
```

实际供应商名称、包标识符和 Team Identifier 必须符合你的预期。记下 Team Identifier。它比应用图标或显示名称稳定得多，也更容易区分不同对象。供应商发布更新时，你可以用它进行具体比较。

不要过度解读 `Authority` 这个词。Developer ID 签名表示 Apple 已将证书颁发给注册开发者，并且代码可以沿证书链通过验证。它不表示 Apple 推荐这个产品。Apple 自己的文档明确区分了相关概念：公证是自动化的恶意软件和签名检查，不是 App Review。

Apple 的代码签名指南还提醒开发者，不要使用 `codesign --deep` 为复杂软件签名，因为它会过于广泛地应用选项。这个警告针对的是创建签名。对于用户执行验证流程，`--deep` 很有用，因为它要求 `codesign` 验证嵌套代码。不过，它仍不会检查每个脚本或配置文件是否带有恶意意图。

如果应用包含辅助程序、命令行工具或扩展，验证整个应用包只是第一步，并不是最终结论。本文后面的首次启动审核，才是将这些组件与应用声明的需求进行比较的地方。

## Gatekeeper 评估会检查 macOS 将要运行的版本

即使签名看起来正确，也要对应用执行 Gatekeeper 评估。`spctl` 会根据运行时真正重要的系统策略评估项目。它更接近这样一个问题：“这台 Mac 会在正常保护机制下接受这个应用吗？”

使用：

```sh
spctl --assess --type execute --verbose=4 \\
  \"/Volumes/Vendor Security/Vendor Security.app\"
```

正常结果通常包含类似以下内容的行：

```text
/Volumes/Vendor Security/Vendor Security.app: accepted
source=Notarized Developer ID
origin=Vendor, Inc. (ABCDE12345)
```

确切措辞会因 macOS 版本而异。你需要的是 accepted 评估结果；对于直接下载的应用，来源最好是经过公证的 Developer ID；同时 origin 必须是你认识的发布者。

这项检查能发现校验和无法发现的问题：你可能从错误的发布者那里下载了一个未被修改的文件。它也能发现仅检查证书时可能遗漏的另一类问题：签名有效的应用可能不符合当前 Gatekeeper 策略。

Apple 将 Gatekeeper 描述为会检查下载软件的已识别开发者、公证状态和是否被修改。这是一层有用的安全网，但它仍然只是操作系统的策略评估。它应当支持你的决定，而不能替代你对发布者身份和所请求权限是否合理的判断。

在理解失败原因之前，不要通过删除属性或在 Finder 中强制打开来压制失败的评估。把失败文本保存在审核记录中。证书被撤销、签名过期或格式错误、缺少公证结果，以及组织管理限制，需要采取不同的应对方式。把所有警告都当作普通麻烦，是让例外变成正常安装路径的原因。

## 附加票据很有帮助，但不是完整的公证检查

公证和附加票据有关联，但不是一回事。公证表示 Apple 的公证服务处理了提交的软件，并在自动检查后签发票据。附加票据则是把该票据附加到应用、磁盘映像或安装包上。Gatekeeper 也可以在线查找票据。

如果安装了 Xcode Command Line Tools，可以检查是否附加了票据：

```sh
xcrun stapler validate \"/Volumes/Vendor Security/Vendor Security.app\"
```

验证成功表示票据确实附加在该应用包上。对于可能在没有网络连接时首次启动应用的笔记本，这很有帮助。

不要仅因为命令显示没有附加票据就拒绝应用。Apple 说明过，Gatekeeper 可以在线找到公证票据，包括用户在公证完成前就下载应用的情况。ZIP 压缩包本身也无法携带附加票据。发布者必须先为其中的项目附加票据，再创建新的压缩包。

按正确顺序进行检查：

1. 如果存在独立发布的摘要，先验证 SHA-256 摘要。
2. 验证应用签名并检查 Team Identifier。
3. 执行 Gatekeeper 评估。
4. 如果工具可用，并且首次离线启动很重要，再验证附加票据。

除非发布者明确记录了离线部署流程，否则首次正常启动时让 Mac 保持联网。这样 Gatekeeper 才能执行正常的在线查询，包括证书状态检查。不要把附加票据理解成永久保证，因为签名身份之后仍可能被撤销。

公证的局限值得明确说明。它不是对应用行为的完整审计。它不能确认应用的云服务是否安全处理数据、更新系统是否可靠，或新版本是否只会请求合理权限。它说明提交的文件通过了 Apple 的自动化流程，并为 Gatekeeper 提供了可使用的票据。

## 隔离属性记录来源，因此不要删除

`com.apple.quarantine` 扩展属性记录 macOS 是否通过某个将项目标记为下载或传输的渠道收到它。它帮助 Gatekeeper 识别这是一个值得检查的首次运行事件。它不是恶意软件标签。

使用下面的命令检查属性：

```sh
xattr -l \"/Volumes/Vendor Security/Vendor Security.app\"
```

对于下载的项目，你可能会看到包含以下内容的输出：

```text
com.apple.quarantine: 0083;...;Safari;...
```

标志位和时间戳格式属于实现细节。真正有用的结论是，该项目仍保留下载来源信息。应用带有这个属性，并不代表它可疑。大多数浏览器下载都应当带有它。

没有隔离属性也不代表应用安全。文件经过归档工具、网络共享、可移动介质或同事复制后，扩展属性可能丢失。Apple 还说明，无论软件通过什么方式到达，macOS 都会在首次打开时检查已知恶意内容。在实际使用中，保留来源信息仍能为 Gatekeeper 和审核流程提供更好的上下文。

应用无法打开时，常见的解决方法是：

```sh
xattr -dr com.apple.quarantine \"/Applications/Vendor Security.app\"
```

不要把它作为常规安装流程的一部分。它只是因为你不喜欢控制措施的结果，就移除了一个控制信号。它不会修复损坏的签名，不会确认发布者身份，也不会让未经公证的应用更安全。如果供应商发布的说明要求运行这条命令，请停下来弄清楚，为什么它不能提供一个能通过 Gatekeeper 的版本。

受管理的 Mac 可能通过设备管理执行更严格的规则。在这种情况下，出于合理原因，强制打开可能不可用或被禁止。请向管理员询问策略规定的处理路径，不要把限制当作技术谜题。

## 在 Installer 获得密码前，安装包需要单独检查

与应用包相比，`.pkg` 更需要谨慎，因为 Apple 的 Installer 在获得授权后可以写入用户文件夹以外的位置。安全产品有时需要通过安装包添加特权辅助程序、系统扩展、网络过滤器或支持组件。这些需求可能合理，但仍属于影响重大的操作。

双击安装包前，先检查其签名信息：

```sh
pkgutil --check-signature \"$HOME/Downloads/VendorSecurity.pkg\"
```

典型结果会标明安装包是否已签名、Developer ID Installer 证书以及证书链。将供应商名称和可用的 Team Identifier 与你为应用记录的身份，或与供应商公布的安装材料进行比较。

然后让 Gatekeeper 以安装程序类型评估该安装包：

```sh
spctl --assess --type install --verbose=4 \\
  \"$HOME/Downloads/VendorSecurity.pkg\"
```

不要用应用评估代替安装包评估。它们是不同的文件，在许多版本中使用不同类型的证书签名，打开后也会带来不同后果。

输入管理员密码前，先确认安装程序声称要添加什么。可靠的供应商会说明它是否安装特权辅助程序、系统扩展、登录项、配置描述文件或网络组件。对于要求控制 Mac 上安全相关部分的软件，“需要系统访问权限”这类模糊说法是不够的。

如果安装包还会安装应用，也要在启动应用前检查已安装的应用。已签名的安装包可以合法地包含应用包，但安装包的签名并不能免除对该应用执行签名的验证。

## 首次启动是审批审核，不是抢着点击“允许”

文件通过检查后，把应用移到预定位置，并在你在场时正常启动。应用完成初始设置前，保留原始下载文件和记录的输出。不要因为应用被称为安全产品，就一开始批准所有提示。

把每个 macOS 提示都看作应用对其行为的说明。常见请求包括通知、完全磁盘访问、辅助功能、屏幕录制、网络过滤、系统扩展或经管理员批准的辅助程序。每项请求都应与产品的实际职责有明确联系。

例如，网络检查应用合理地请求网络扩展权限。凭据操作网关可能需要管理自己的保管库并连接到指定服务，但不应因此需要通用的屏幕录制权限。磁盘扫描器可能需要完全磁盘访问，但它应说明会读取什么，以及哪些内容仍保留在本地。产品类别本身不能带来无限授权。

使用下面这份简短的首次启动审核清单：

- 将每个提示与文档中记录、且你确实准备使用的功能对应起来。
- 遇到意外请求时先暂停，在批准前检查供应商文档。
- 在确认要安装的组件名称和用途前，不要提供管理员凭据。
- 设置完成后检查系统设置，查看是否新增了登录项、描述文件、扩展或后台项目。
- 将应用版本、Team Identifier、下载哈希和权限决定与版本记录放在一起。

后两点很重要，因为代价最高的批准往往不是应用权限，而是经管理员批准、会在应用窗口关闭后继续存在的组件。它们获得的访问权限可能比应用本身还要多。

对于团队部署，把这项审核放进简单的接收文档中。记录来源位置、日期、文件哈希、签名身份、Gatekeeper 结果、公证结果、已安装组件、批准的权限和审核人姓名。这样后续审核更新会快得多，也能在意外的签名身份变化到达每台开发者笔记本之前发现问题。

## 不要把安装干净与运行模式可信混为一谈

干净的签名、通过的 Gatekeeper 评估和合理的权限集合，是一个好的起点。但它们不能证明应用启动后一定会做出安全选择。你仍需评估它在哪里存储秘密、如何验证更新、会把什么数据发送到 Mac 外部，以及被入侵的进程是否能滥用它获得的权限。

对于面向代理的安全工具，要坚持建立能够承受代理犯错的边界。代理不应仅仅因为要执行某项操作，就获得可重复使用的凭据。它应通过本地控制点请求操作，由该控制点要求人工批准并保留审计记录。

我对 Sallyport 也采用同样的安装标准：先验证已签名的版本，再评估它的实际边界。Sallyport 将凭据保存在加密保管库中，由自己执行获批准的 HTTP 或 SSH 操作，而不是把秘密传给代理进程。

有用的习惯很简单：不要因为应用被标记为安全软件，就让它获得广泛权限。要求它展示发布者身份、未被修改的内容、Gatekeeper 状态以及它确切需要的访问权限。如果它无法经受这套审核，就不该获得让安全应用变得强大的那些权限。
