# macOS 上针对 AI 代理审查的审批提示欺骗

审批提示之所以容易成为攻击目标，是因为它能把复杂的技术攻击变成简单的人类下意识反应。只要能让审查者批准一个看起来普通、紧急又熟悉的请求，攻击者就不必窃取凭据。

当自动编程代理可以调用 API 或运行 SSH 命令时，问题会更加严重。审查者可能同时面对拉取请求、终端、聊天窗口和一堆通知。如果审批界面没有清楚显示来源和范围，审查者就只能根据一个缩略图来完成安全工作。

解决办法不是教人记住每一种 macOS 对话框。对于一个研究过相同截图的人来说，视觉记忆比不上精心制作的仿冒窗口。审查者需要一套可重复的方法，把审批绑定到三个事实：发起请求的进程、即将执行的操作，以及收集审批的可信位置。

## 熟悉的窗口不能证明它属于谁

一个看起来像系统提示的窗口，在与正在运行的进程和具体请求建立联系之前，也只是一组像素。圆角、熟悉的按钮顺序、菜单栏图标、Touch ID 提示，以及类似 Apple 的句子，都不能证明来源。它们只能说明有人知道 macOS 的界面长什么样。

这一区别说出来很简单，但审查习惯经常把它们混在一起。人们会问：“它看起来像我上周见过的提示吗？”更好的问题是：“哪个进程触发了这个请求？不依赖这个窗口，我可以在哪里检查那个进程？”

macOS 为用户提供了有用的软件来源控制。Apple 在 macOS 文档的 App code signing process 中解释，Developer ID 签名可以让用户确认软件自开发者签名后没有被改动。Apple 还将这一点与公证区分开来，公证会检查提交的软件是否含有已知的恶意内容。这些控制措施很重要，但它们都不能让已签名应用显示的每句话自动变成可信的授权请求。

恶意程序可能没有签名，也可能有签名。合法程序可能已经遭到入侵。合法程序也可能提出与当前任务毫无关系的操作。对话框的视觉设计无法回答这些问题。

在确认情况之前，把任何审批界面视为以下四类之一：

- macOS 自有的同意或身份验证界面。
- 你原本想使用的应用所拥有的窗口。
- 提醒某个应用需要你关注的通知。
- 仿照前三类界面制作的网页或覆盖层。

第一类可能使用系统控件，但不要仅凭外观做出审批决定。其余几类都属于应用内容。它们可能有用，但需要比“我认得这个按钮的形状”更可靠的审查路径。

## 每次审批都必须绑定执行者、操作和渠道

只有当审批同时绑定执行者、操作和渠道时，审查者才能做出可靠决定。大多数薄弱的提示只说明其中一项，然后让人猜另外两项。

执行者是发起请求的本地进程。对于代理工作流来说，它不只是“编程助手”，而是为本次运行启动的具体进程，并且在工作流能够提供时，还应带有可识别的签名权限。如果请求只写着“AI 助手需要审批”，它就隐藏了对审查者最有用的信息。

操作是实际要执行的动作。如果实际操作是向指定 API 端点创建生产用户，“允许网络访问”就算不上操作描述。如果命令可能只是读取状态，也可能执行破坏性的远程部署，“批准 SSH”同样不够。审查者需要看到动词、目标，以及工具能够如实说明的后果。

渠道是决定通过什么地方传递。桌面通知是一种传递机制，浏览器页面可能是应用界面，原生应用窗口可能是正确的控制平面。但这些名称本身都不能保证安全。审查者需要一个预期渠道，并且能够独立重新打开它。

实际情况可以这样对比。

薄弱的请求：

> 部署助手需要权限。允许吗？

可审查的请求：

> 进程：从 Terminal 启动的已签名本地代理运行
>
> 操作：向 `api.example.internal` 发送 POST `/v1/releases`
>
> 权限：生产部署凭据
>
> 范围：仅限本次请求
>
> 决策渠道：正在运行的审批应用

第二个请求仍然可能应该被拒绝。这没有问题。它的作用不是让审批感觉安全，而是清楚展示所请求的权限，让人能够做出决定。

这也暴露了团队在“可信代理”问题上的一个误区。信任执行者，不等于允许它执行所有操作。进程可能很熟悉，但请求的目标可能不对。目标可能符合预期，但凭据范围可能过大。安全的审查流程会把这些事实放在一起，而不是把审批变成笼统的认可。

## 通知应该召集审查，而不是收集同意

通知是一种打断机制，不是可靠的授权界面。它的空间有限，要和桌面上的所有其他提醒竞争，还可能包含专门推动用户快速点击的文字。如果通知写着“紧急：请在部署过期前批准”，它就已经在施加压力，而你还不知道是谁创建了这条消息，甚至不知道是否真的存在任何部署。

Apple 的 Notifications 设置允许用户控制预览、通知是否出现在锁定屏幕上，以及在共享或镜像显示屏时是否显示通知。Apple 也允许用户按应用管理权限和显示方式。这些控制能减少不必要的暴露，但不能把通知文字变成可信的交易记录。

审查者只能把通知当作指引。阅读应用名称，记下时间，然后从已知位置打开预期的应用。如果请求真实存在，应用应该显示相同的待处理操作，并提供足够的背景供你判断。如果通知打开的是网页、其他辅助应用，或一个没有对应待处理请求的窗口，就应当停止。

不要教审查者因为通知图标看起来正确就批准。图标很小，可能不熟悉，也可能与相似名称混淆。真正重要的问题是，通知是否能带你回到工作流中预期使用的控制界面。

对于可能在锁定屏幕上暴露敏感目标、账户名称或请求细节的工具，请关闭通知预览。当会议参与者不应看到这些内容时，在共享屏幕期间关闭通知。这些属于保密设置，不是反欺骗控制，但它们能减少压力和信息泄露的来源。

通知尤其不适合高影响操作，因为它迫使审查者在获得背景信息之前做决定。正确的流程应该有意放慢：收到通知，打开预期的应用，检查请求，然后批准或拒绝。增加这一步并不是为了制造阻碍，而是为了打断攻击者同时控制消息和点击路径的企图。

## 覆盖层利用的是注意力，而不是 macOS 漏洞

仿冒审批窗口不需要突破 macOS 的安全控制也能造成危险。普通应用可以绘制普通窗口，将它放在另一个应用上方，使用相似的标题，并安排在一个可信的触发动作之后出现。攻击能够奏效，是因为审查者替它补上了缺失的来源信息。

常见的说法过于简单：“假的提示一定是拥有特殊屏幕权限的恶意软件。”有时恶意软件确实会寻求广泛权限，但令人信服的覆盖层并不总需要这些权限。任何应用都能显示自己的内容。浏览器标签页可以显示看起来像同意页面的网页。远程支持会话也可能在操作者控制叙事的同时，要求用户批准某一步操作。

不要把查看屏幕的能力和创建窗口的能力混为一谈。Apple 将 Screen & System Audio Recording 视为 Privacy & Security 权限，用户可以针对单个应用和网站允许或拒绝该权限。它控制的是屏幕和音频内容的捕获，不会认证某个提示。授予该权限也不能说明眼前窗口属于谁。

对 Accessibility 权限也应保持同样的纪律。合法软件可能确实需要与界面元素交互，但审查者不应因此推断，拥有 Accessibility 权限的应用所显示的每个窗口都属于系统。权限历史可以帮助调查，却不能在审批当下充当证据。

有效的覆盖层审查应先看行为，而不是装饰：

- 请求是否紧接着你主动执行的操作出现？
- 切换离开并重新打开预期应用后，是否还能看到同一个请求？
- 请求是否以符合当前工作的方式写明目标和范围？
- 它是否要求输入密码、恢复短语或该工作流本不应需要的秘密？
- 它是否试图阻止你在其他地方检查请求？

最后一项能抓住比人们想象中更多的恶意提示。真实的控制流程应该能经受几秒钟的验证。欺骗性提示往往会制造倒计时，声称离开窗口请求就会失败，或者让审查者觉得任何犹豫都会造成损失。

## 可信的仿冒提示往往有一套可识别的顺序

设想一名工程师要求代理更新预发布环境。代理的终端输出说，它需要批准才能调用 API。几乎同时，一条通知出现了：“需要部署授权。打开 Security Center。”

工程师点击通知。一个窗口打开了，标题看起来像熟悉的 macOS 安全面板。窗口包含简短说明、一个以预期公司域名开头的目标，以及标有“取消”和“允许”的按钮。它甚至提到了 Touch ID，尽管窗口从未真正触发生物识别验证。

如果工程师接受了这个视觉叙事，欺骗就成功了。问题可能同时出现在多个地方：

1. 通知可能来自与审批应用不同的应用。
2. 点击通知后可能打开的是网页或辅助工具，而不是真正的审批控制界面。
3. 显示的目标可能是相似域名、重定向地址，或范围宽泛的账户权限，而不是本次部署请求对应的目标。
4. 启动代理的进程可能与请求审批的进程不同。
5. 点击之后可能没有留下持久记录，让审查者无法还原自己批准了什么。

这就是为什么审查流程必须经得起视觉上的完美仿冒。工程师应当让窗口保持打开，不要按任何按钮，然后通过菜单栏或 Applications 文件夹调出预期的审批应用，查看当前代理运行是否生成了待处理请求。如果没有匹配请求，弹窗不会因为做得精致就更可信。

如果存在匹配请求，就比较两边的描述。合法请求应当指向相同的操作、端点和权限。如果预期应用显示的是 SSH 命令，而弹窗描述的是笼统的部署审批，在弄清差异之前，应拒绝两者。攻击者会从审查者把部分匹配当成确认中获利。

令人不安的是，仿冒提示经常使用真实细节。它可能知道项目名称、环境名称和真实部署发生的时间。这些细节只能证明攻击者掌握了一些背景信息，不能证明所请求的操作确实属于预期进程。

## 在需要审批之前验证已安装的应用

当窗口声称操作将在十秒后过期时，你无法从容地完成身份验证。应在设置期间确认预期应用的身份，把信息记录在团队文档中，并在普通工作日测试验证流程。

对于安装在已知路径中的应用，下面的命令会输出 macOS 看到的签名信息：

```sh
codesign -dvvv "/Applications/Expected Approval App.app" 21 | grep -E 'Identifier=|TeamIdentifier=|Authority='
```

输出大致如下：

```text
Identifier=com.example.approvals
TeamIdentifier=ABCDE12345
Authority=Developer ID Application: Example Software, Inc. (ABCDE12345)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
```

不要把这些示例值直接复制到清单中。通过团队正常的软件流程获取应用后，记录你们批准的软件对应的 identifier 和 team identifier。显示名称是较弱的证据，因为多个应用可以使用同一个名称。Bundle identifier 和签名团队能提供窗口标题无法提供的事实。

再用下面的命令检查已安装软件包的评估状态：

```sh
spctl -a -vv "/Applications/Expected Approval App.app"
```

典型输出会包含 `accepted` 之类的评估结果，以及来源和出处。不同 macOS 版本和分发方式的措辞可能不同，因此不要编写一个因为期待某句完全相同的文本就失败的脚本。把它当作调查辅助工具：意外的拒绝结果、来源、路径或签名身份，都足以让你停止并展开调查。

Apple 的 Gatekeeper 文档解释说，在默认设置下，macOS 会检查下载的软件是否有已识别的开发者、公证信息，以及是否被改动。这能保护启动路径，是必要的安全措施，但不能替代检查应用当前请求的操作是否正确。

团队应保存一份简洁记录，包括应用名称、正常安装位置、Bundle identifier、签名团队和更新负责人。把它放在与环境名称和事件联系人相同的内部位置。更新导致其中任何信息发生变化时，应把审核变化纳入更新流程，而不是等到审批事件发生时才重新确认。

不要采取“它在 Applications 里，所以一定是正确应用”这种偷懒做法。Applications 是一个位置，不是身份声明。复制出来的或名称相似的软件包也可以放在那里。路径给你一个稳定的对象进行检查，签名提供身份声明，而团队流程决定这个身份是否符合预期。

## 通过独立路径审查请求

审批出现时，每次都使用同一套简短流程。普通请求应在一分钟内完成，即使第一个窗口完全是假的，这套流程也应该有效。

1. 第一次看到提示时先停下来。在确认是什么打开了它之前，不要点击允许、关闭，也不要打开嵌入式链接。
2. 用简单的话说出预期操作。例如：“我要求预发布代理读取部署状态。”如果请求描述的是生产写入、SSH 会话或广泛的账户访问，你已经发现了不一致。
3. 通过预期的菜单栏项目、Applications 位置或已知启动器，独立打开审批应用。不要把可疑窗口当作导航工具。
4. 找到待处理请求，比较发起进程、操作、目标、权限和范围。仅仅匹配进程名称并不够。
5. 只批准完成当前任务所需的最小请求。对于不清楚、范围过大或与主动发起的工作不一致的请求，应予拒绝。

这套流程也能避开一个流行但糟糕的建议：“先批准一次熟悉的代理，这样它就不会再打扰你。”这个建议之所以流行，是因为重复提示令人烦躁，代理也经常需要执行多次相关调用。但它是错误的，因为提示过多是设计问题，不是取消审查边界的理由。

更好的设计可以在当前运行期间授权一个已知进程，同时让敏感权限继续需要单独确认。这样，审查者可以少做重复决定，却不会把永久权限授予一个含糊的工作类别。

Sallyport 直接采用了这种拆分方式：新的代理进程会收到会话授权请求，其中标明进程的代码签名权限，而某个凭据可以设置为每次使用都需要审批。代理永远不会拿到秘密本身，因此审批决定的是应用是否执行操作，而不是把凭据交给代理。

在真实事件中，这一区别很重要。如果某次代理运行开始出现异常，应撤销它的会话，不要等代理触发下一次审批。会话边界让审查者能够阻止该进程继续执行，而不必假设之前批准的每项能力现在仍然合适。

## 审批措辞应先说明范围，再制造紧迫感

团队经常花太多时间润色警告文字，却很少先确定审批必须携带哪些事实。薄弱提示换成更好的句子，仍然很薄弱。带有必要事实的朴素句子反而更安全。

先写操作。“在生产环境创建发布”能告诉审查者将发生什么。“授权部署工作流”则把操作藏在内部标签后面。

接着写目标。主机名、代码仓库、账户或远程主机能告诉审查者权限将被用于哪里。如果系统不知道目标，无法给出具体名称，就应明确说明，而不是用“可信服务”这类令人安心却毫无信息的短语来填补背景。

用人能理解的方式说明范围。允许对已知端点发送一次 `GET` 请求，与允许凭据在整个会话期间用于所有调用，性质完全不同。批准一次 SSH 命令，也不同于允许不受限制的 Shell。提示应描述真实边界，而不是描述一个理想中的边界。

如实说明审查者看不到的部分。如果工具只知道代理将发起 SSH 连接，就应该这样写，而不是假装自己检查过每一条远程命令。虚假的精确度会训练人们去信任那些与实际执行边界不对应的标签。

尤其要避免以下含糊的操作名称：

- “继续”
- “授予访问权限”
- “完成设置”
- “验证身份”
- “允许代理运行”

每个短语都要求审查者根据背景自行补充含义。如果这些背景来自攻击者控制的终端消息、聊天消息、通知或弹窗，审查者就已经成了欺骗的一部分。

可靠的审批流程也应让拒绝成为正常结果。“拒绝”不应看起来像危险的例外。它应该停止操作，保留足够信息供审查者稍后检查，并允许用户在修正请求后重试。当拒绝看起来会造成灾难时，人们就会为了摆脱困境而批准。

## 日志能把怀疑变成调查

发生可疑审批后，不要再把屏幕当作证据。屏幕会消失，窗口可能撒谎，记忆也会很快自行改写。保存运行过的进程、请求的操作、审查者的决定，以及操作是否到达目标的记录。

先记录时间范围。然后保存代理记录、与本次运行相关的终端历史，以及审批工具的操作历史。记录显示请求的应用的确切路径。如果可疑内容来自通知，记下通知显示的应用名称，以及打开后进入的是浏览器、辅助应用还是预期应用。

不要一开始就删除可疑应用。如果需要为内部调查保留证据，先记录其路径和签名信息。如果机器可能已经遭到入侵，应按照团队的事件流程处理，并在适当情况下隔离设备。审批提示欺骗可能是社会工程攻击、本地不需要的应用、浏览器遭入侵，也可能说明合法工具存在设计缺陷。证据能告诉你究竟是哪一种问题。

对于代理操作，日志需要分成两个层级。一条记录应描述整个运行，并让操作员能够撤销它。另一条应描述单独的调用，包括目标和决定。没有第一类记录，你无法干净地停止正在运行的执行者。没有第二类记录，你无法确认某次审批究竟启用了什么。

Sallyport 会从一份加密的哈希链审计日志中投射会话和单独的活动记录，`sp audit verify` 可以在离线状态下对密文验证这条链。这不会让错误审批变得无害，但它能让审查者和调查人员在弹窗消失后，仍然得到最重要问题的可靠答案：这个进程实际执行了什么操作？

目标不是让每个审查者都成为 macOS 安全专家，而是让安全行为变得自然：离开可疑界面，重新打开预期的控制平面，比较事实，只批准符合当前工作的请求。经不起这套检查的提示，不值得点击。
