# 保险库锁定与屏幕锁定如何改变代理测试

锁定的屏幕和锁定的保险库是两种不同的控制措施。把它们当成同一种控制，测出来的结果几乎没有意义。macOS 屏幕锁定限制的是用户会话的交互式访问。保险库锁定则必须在凭据即将被使用的节点阻止受保护的操作，包括阻止此前已经获批、在你离开后仍继续运行的进程发起的操作。

这种区别对自主编程代理尤其重要。代理可以让进程保持运行、保留上下文、安排任务，并重试失败的请求。只确认人在桌面前无法输入，并不能证明这个进程仍然无法调用 API 或打开 SSH 连接。应分别测试这两种状态，再测试它们重叠时的情况。

我见过一些团队把这叫作“锁定电脑”测试，只截一张锁屏截图，就宣布工作完成了。那只能证明屏幕锁定存在，不能证明凭据周围有一条绝对拒绝边界。

## 锁定的显示器无法决定进程能否执行操作

macOS 屏幕锁定保护的是控制台会话，防止拥有键盘和显示器实体访问权限的人进行操作。它本身并不能说明该会话下已经运行的每个进程会怎样，也不能说明这些进程拥有的每个网络连接会怎样，更不能说明应用可能使用的每个凭据存储会怎样。

Apple 的 Platform Security 文档把用户身份验证和会话保护，与钥匙串项目等机密的保护分开说明。即使你的部署比 Apple 文档涵盖的场景简单，这种区分在这里仍然很有用。“屏幕是否锁定”和“代理能否使用凭据”是两个不同的问题，需要不同的证据。

屏幕解锁时，代理可能没有风险；但一旦持有者令牌进入自己的环境，它在屏幕锁定后仍可能带来风险。反过来，代理也可能在锁定的会话中运行，却无法发起受保护的请求，因为凭据从未进入它的内存，而且操作网关会拒绝请求。屏幕状态无法告诉你采用的是哪一种设计。

评审时可以使用下面这个实用定义：

- 屏幕锁定控制 Mac 的交互式使用。
- 保险库锁定控制受保护的操作是否可以执行。
- 会话授权控制这个代理进程在其生命周期内是否可以请求受保护的操作。
- 逐次调用密钥控制某次特定的凭据使用是否必须经过人工批准。

这些控制措施在演示中可以很好地配合，但生产环境不应依赖它们始终同时处于理想状态。

## 保险库闸门需要绝对拒绝测试

绝对保险库闸门意味着，保险库锁定时，所有会使用已存储凭据的操作都会被拒绝。拒绝必须发生在注入 HTTP 凭据之前，也必须发生在 SSH 辅助程序进行身份验证之前。它还必须适用于此前已经成功过的进程，而不只是适用于从未被信任的新进程。

Sallyport 明确规定了这条边界：保险库锁定期间，所有操作都会被拒绝。在受支持的 macOS 硬件上，保险库闸门由 Secure Enclave 和 Touch ID 通过硬件控制。这比“我离开时代理不应该调用这个工具”之类的约定更强，也更清晰。

最能说明问题的测试要从成功开始。配置一个由你控制、后果很低的端点，例如只记录请求方法和不透明请求标识符的 HTTP 端点，或者一台执行固定命令并输出固定单词的 SSH 主机。不要从生产部署端点开始。你需要一个可以检查日志的目标，同时不能暴露真实密钥，也不能改变重要内容。

然后让代理进程保持运行，通过应用锁定保险库，再次请求同一操作。正确的结果包含三部分：

1. 调用方收到清晰的拒绝，而不是超时或笼统的网络错误。
2. Activity 日志将这次尝试记录为已拒绝，并提供足够的信息来识别会话和凭据路径，但不暴露凭据本身。
3. 受控端点或 SSH 主机没有收到新的请求或命令。

第三部分可以抓住那些被精致用户界面掩盖的问题。如果网关把检查放在了错误的层级，它可能已经发出经过身份验证的请求，之后才显示拒绝。真正重要的证人是远端日志。

对于 SSH，不要使用 `ssh host true` 这类测试，除非你确定测试主机不会接受正常环境中的其他身份。使用代理实际会采用的通道，连接到隔离账户，并为它配置一条在服务器日志中一眼就能认出的命令。如果无法证明连接使用的是哪个身份，你测试的其实是 shell 配置，而不是保险库边界。

## 之前的授权不能越过闸门继续有效

逐会话授权回答的是一个更窄的问题：这个代理进程在本次运行期间是否可以使用受保护的操作？它不是永久授权，也不能覆盖锁定的保险库。

团队经常在这里把便利和权限混为一谈。他们批准代理，看到几个调用成功，锁定保险库，之后又解锁保险库。代理恢复工作后，他们凭感觉判断之前的授权应该继续有效还是应该消失。应根据实际控制模型决定行为，然后进行测试。应用记录的决策顺序把保险库闸门放在第一位。闸门关闭时，优先服从关闭状态。

在不重启代理的情况下执行下面的流程：

1. 启动一个全新的代理进程，执行一次受保护的调用。出现提示时批准它的会话。
2. 使用同一个普通凭据执行第二次受保护的调用。确认无需再次显示会话卡即可成功。
3. 在进程保持运行时锁定保险库。再次执行调用，确认在远端目标收到请求之前就被拒绝。
4. 通过要求的本地交互解锁保险库。再次执行调用，并根据文档记录已建立的会话是否恢复。
5. 退出代理进程，启动另一个全新的进程，执行同一调用。确认这次运行会收到自己的授权卡。

第四步不是表面细节。它能暴露产品是否错误地把“保险库曾经打开过”当成“所有正在运行的代码都再次获得信任”。第五步能暴露进程标识符、终端父进程或宽泛的客户端标签是否变成了意外的长期授权。

授权卡应首先显示进程的代码签名主体。进程名称很容易复制，路径也可能误导人。代码签名主体能让操作人员在代理请求使用密钥时，有更稳定的信息可供判断。记录测试期间授权卡显示的内容，包括当一个不受信任的客户端副本发起同样请求时，你本应看到什么。

## 锁屏测试有不同的目的

在确认保险库行为后，再测试 macOS 屏幕锁定，因为它要回答的是另一个操作问题：无人处于控制台前时，哪些工作可以继续，以及你如何重新取得控制权？

首先，让保险库保持预期的可用状态，并启动一个已经获得授权的会话。触发一次无害的受保护操作，锁定 macOS 屏幕，等待足够长的时间以覆盖你对无人值守工作的正常担忧，然后检查这次操作是否能够继续。不要想当然地判断结果。Mac 的电源设置、网络状态、进程生命周期以及应用自身的闸门都会影响结果。

接着，在锁定屏幕之前先明确锁定保险库，再进行同样的实验。结果应该更简单：受保护操作的尝试会被拒绝。如果代理仍能生成文本或编辑本地文件，这不在当前断言范围内。当前断言是，它不能把存储的 API 密钥或 SSH 密钥转化为外部操作。

这一组测试能把一个令人不安但合理的策略选择，与安全漏洞区分开来。有些开发者会有意允许已经获批的代理在显示器锁定期间继续进行低风险工作。另一些开发者则要求每次离开时都锁定保险库。这些属于运行策略。保险库已锁定后仍允许代理使用凭据，则是边界失效。

不要站在房间另一端看着锁屏来测试。记录代理请求、受控端点和 Activity 日志中的时间戳。开始于屏幕锁定之前的请求，可能在锁定之后完成。这并不能证明它是在锁定之后开始的。顺序很重要。

## 逐次批准适用于你不愿批量批准的操作

逐次调用密钥要求人工批准该凭据的每一次使用。它应当位于已获批准的会话之上，不能替代会话检查，也不能削弱保险库闸门。

即使大多数密钥使用普通的逐会话批准，也应在测试计划中加入一个逐次调用凭据。选择一个效果明显且可逆的目标操作，例如让测试端点递增一个可丢弃的计数器，或让 SSH 命令在临时目录中创建文件。目的是证明应用会在第二次使用时再次询问，并且拒绝提示后，外部操作确实不会发生。

在屏幕解锁状态下执行以下测试，这样可以把提示行为与会话状态分开：

- 启动新的代理进程并批准其会话。
- 使用逐次调用凭据并批准这次调用。确认远端出现一次事件。
- 再次调用同一凭据并拒绝批准。确认远端没有第二次事件。
- 锁定保险库后再调用一次。保险库拒绝应在不显示任何有实际意义的批准选择的情况下发生，因为闸门不允许该操作。

“让每个凭据都逐次批准”这个常见建议听起来很谨慎，因为它会让每次操作都变成人眼可见的决定。实际上，这会训练人们不阅读内容就批准重复出现的授权卡。应把它留给那些单次使用会改变资金、访问权限、发布状态或其他你需要亲自确认的结果的凭据。对于常规调用，有意义的会话批准加上可锁定的保险库，能让操作人员面对更少、更清晰的决定。

## 远程日志能捕捉本地测试遗漏的拒绝

本地错误消息只能证明调用方看到了错误。它不能证明经过身份验证的请求没有离开设备，也无法说明稍后到达的重试请求。

构建一个能报告短请求标识符的测试目标。只要网关配置的凭据路径允许这种请求形式，代理操作就可以发送一个无害的请求头或请求体字段，例如 `test_run=screen-vault-01`。在接收端记录到达时间、方法、通常能够获取的来源信息以及该标识符。让目标与生产数据隔离。

测试记录应包含类似下面的小表格：

| 尝试 | 保险库状态 | macOS 屏幕 | 预期远程事件 | 观察结果 |
| --- | --- | --- | --- | --- |
| A | 可用 | 解锁 | 一个事件 | 已记录事件 |
| B | 锁定 | 解锁 | 无 | 无事件，本地拒绝 |
| C | 可用 | 锁定 | 取决于你的运行策略 | 记录实际结果 |
| D | 锁定 | 锁定 | 无 | 无事件，本地拒绝 |

C 行中的“取决于”是有意保留的。不要把策略决定隐藏在通过或失败的列中。明确说明在你的环境中，屏幕锁定而保险库仍可用时，已获批准的进程是否可以继续工作，并让预期结果与该决定保持一致。

对于 HTTP，如果反向代理可能在应用看到请求之前拒绝或重试请求，应检查应用日志，而不只是访问日志。对于 SSH，应检查隔离账户的服务器身份验证记录和命令记录。每次测试都使用新的标识符。重复使用同一个标签，会在你需要清晰结果时制造关于延迟交付的争论。

## 审计记录应该解释状态变化

良好的审计轨迹能让审查人员还原谁运行了代理、代理尝试了哪些操作，以及网关拒绝了哪些操作或执行了哪些操作。审查人员不应该需要猜测，缺少远程事件究竟是因为保险库锁定、网络连接丢失，还是代理根本没有发出请求。

应用会为代理运行维护 Sessions 日志，为单独调用维护 Activity 日志。两者都来自一份写入盲加密、采用哈希链的审计记录，因此它们是同一底层记录的两个视图，而不是会讲出不同故事的两份竞争日志。测试发现两份日志不一致时，在能够解释原因之前，应将其视为测试失败。

每次测试运行后，在测试记录中保留以下观察结果：

- 授权时显示的会话身份，以及你批准或拒绝它的时间。
- 保险库状态和屏幕状态发生变化的准确时间。
- 每次尝试操作时，调用方返回的成功或拒绝结果。
- 对应的 Activity 和 Sessions 条目。
- 受控目标证明自己是否收到该操作的证据。

然后使用以下命令离线验证链：

```
sp audit verify
```

成功运行后应该会报告验证通过，不过具体措辞可能因版本而异。真正有用的特性是，验证可以在密文上进行，而且不需要打开保险库。这样，你就能把受保护的审计文件交给需要检查是否遭到篡改的审查人员，而无需给他们凭据，也无需让他们具备执行操作的能力。

不要夸大这个命令的作用。有效的哈希链只能说明，在验证器模型下，记录的顺序保持了完整性。它不能证明你选对了远程日志，不能证明时钟准确，也不能证明测试目标没有其他进入路径。应将它与远程证据结合起来。

## 进程生命周期是人们经常跳过的边界

代理进程退出时，会话授权也应该结束。应明确测试这一点，因为“我打开了一个新终端”不等于“之前的进程已经结束”，而后台监控程序可能会在用户界面消失后继续保留子进程。

从干净的基线开始。退出代理进程，用你平时的进程检查方式确认它已经消失，然后通过同一客户端路径启动另一个进程。它第一次执行受保护调用时，应该创建新会话并请求授权。如果它悄悄继承了授权，应查明究竟是哪种身份携带了这项授权。这可能是有意设计的功能，但它代表的权限范围远大于逐会话授权通常所暗示的范围。

还要测试启动失败和客户端副本。如果客户端进程启动、请求批准、在执行操作前退出，而替代进程却可以使用这项批准，你就找到了生命周期漏洞。如果另一个签名不同的进程使用相同的易混淆名称，授权界面必须提供足够的权威信息，让操作人员能够区分它们。

这不是假想的记录问题。代理工具发展很快，包装器会生成辅助进程、重新连接传输层，并从崩溃中恢复。真正发起受保护调用的进程，才是需要授权的对象。项目名称或聊天记录不是执行身份。

## 将睡眠、重启和网络丢失作为独立实验

人们经常在笔记本保持唤醒时测试屏幕锁定，然后假设结果也适用于睡眠、重启和网络中断。事实并非如此。每个事件都会改变系统的不同部分。

测试睡眠时，记录代理进程是否仍然运行、Mac 是否允许你使用的网络路径继续工作，以及保险库状态是否发生变化。测试重启时，把每次代理运行都视为新的运行，并要求受保护操作前先进行新的本地解锁。测试网络丢失时，确认失败的调用不会在保险库锁定后，或原始进程退出后，触发不安全的重试。

让这些实验保持小而明确。一个有用的重试测试只使用一个请求标识符和一个能告诉你收到零次、一次还是多次请求的接收端。在受控的时间点启动请求、移除网络路径、锁定保险库、恢复网络路径，然后观察是否有延迟交付。如果请求会产生副作用，请使用可丢弃的接收端。重试普通读取操作，与重试改变状态的操作并不相同。

不要把这变成测试文档中的通用策略引擎。问题其实更具体：正常的计算机事件打断代理时，固定的决策顺序是否仍会产生预期的拒绝状态。清晰的证据胜过由大量想象规则构成的庞大矩阵。

## 完成的测试计划应该留下一个明确无歧义的结论

完成后的计划应该让你能够用证据说明，锁定的 macOS 屏幕和锁定的保险库已经分别测试过。它还应当显示，已获批准的进程在无人看管时是否可以执行操作，逐次批准是否阻止了第二次使用，以及被拒绝的调用是否留在远程系统之外。

用直白的语言写出最终断言：“保险库锁定时，无论 macOS 屏幕处于锁定还是解锁状态，代理都无法使用配置好的 HTTP 或 SSH 凭据。”附上远程目标记录和审计验证结果。然后单独补充你的运行策略决定，说明只有屏幕锁定时，已获批准的代理可以执行哪些操作。

最后这句话能避免事件审查中常见的混淆。有人可以不同意你的无人值守工作策略，但不应把这项策略误认为保险库闸门已经正常工作的证据。
