跳转到内容

第 53 章 秘密、权限、租户隔离与供应链安全

Mira 查询订单 A-104,助手返回“已发货”。状态看起来完全合理,却属于另一个租户中的 Leo。句子里没有明显的越狱内容,内容安全过滤器也可能认为没有问题。真正的缺陷是查询取错了人的记录。

另一种失败同样可能看起来很普通:一个好用的 Skill 改了一行,开始读取秘密文件,工具名却仍在允许列表里。这些问题无法靠答案质量解决:当前使用谁的权限,权限覆盖哪些对象,获准运行的究竟是哪一份代码或资产?本章沿同一个本地助手回答这些问题,完成 KA-8,不另建多 Agent 平台。

53.1 身份由宿主确定,不由提议参数决定

Section titled “53.1 身份由宿主确定,不由提议参数决定”

租户(tenant)是需要独立管理、隔离数据访问的一组使用者。新增的版本化夹具中有两个租户:north 中,Mira 的 A-104 状态为 processing;south 中,Leo 的 A-104 状态为 shipped。订单 ID 重复是有意安排的。文中用 Mira、Leo 称呼这两个人,实际用户 ID 分别是 mira、leo。前面只读示例的数据保持不变。

例 53.1 知道 ID,还不足以决定能否访问。 Mira 的可信宿主上下文给出租户 north、用户 Mira、目标服务 orders-local,以及权限范围 orders:read:own。工具只允许传入 order_id: A-104。正确查询必须同时匹配租户、订单 ID 和所有者:

SELECT * FROM orders
WHERE tenant = ? AND id = ? AND owner = ?;

参数依次绑定 north、A-104、mira。这里的参数是与 SQL 结构分开传入的数据值,模型不负责编写这条查询。正确结果是 processing。故意不安全的查询却从不可信内容中接受 south,于是返回 shipped。如果仍允许模型决定身份,即使补上所有者检查,也没有解决根本的信任问题。

受控分发器拒绝额外的 tenant 参数;存储层则独立使用可信身份中的租户和所有者限定查询。实测还分别把测试身份改成 north/Leo 和 south/Mira,两者都因不是对应记录的所有者而失败。合法的 south/Leo 身份则能得到 shipped。隔离的目标是正确分开访问,而不是让 south 也无法正常使用服务。

模拟器中的访问主体(principal)是宿主创建的身份记录。能在进程内执行任意 Python 的程序也能构造它,所以这不是一个真实认证服务。在生产环境中,必须先验证凭据,建立可信请求上下文,再执行这些检查。模型说“我是 Mira”,不构成这种验证。

53.2 只在需要行使权限的位置使用凭据

Section titled “53.2 只在需要行使权限的位置使用凭据”

秘密(secret)是披露后会突破某项边界的信息;凭据(credential)是用于建立访问权限的证明。访问令牌等秘密同时也是凭据。第 52 章的虚构柜门密码用来展示披露限制,不是本应用的认证机制。

凭据应通过宿主控制的路径交给需要它的组件,不应复制进模型上下文、检索文档、Skill 指令或日常日志。令牌的目标服务字段说明它发给哪个服务使用;权限范围说明允许做什么;有效期限制可以使用多久。把发给一个服务的令牌传给另一个服务,并不会使它自动有效。

表 53.1 本地助手的权限矩阵。 这些是实际执行的模拟规则,不是真实 OAuth 授权。

身份与请求是否自动执行额外条件实际行为
有效的 north/Mira 只读身份,查询自己的 A-104是目标服务、权限范围、租户和所有者均正确返回 processing
只读身份,取消自己的 A-104否需要取消权限和匹配审批因缺少写权限而拒绝
可写身份,取消自己的 A-104获批后才可执行精确操作、主体、租户、版本和有效期均匹配有效审批产生一次副作用
可写身份,但审批缺失、被拒或过期否没有有效审批副作用为零
具有审批角色和权限的 north 审查人可以建立审批记录同一租户,明确记录决定能批准或拒绝,不因此获得查询或取消订单的权限
凭据过期、目标服务错误、缺少读取权限或所有者不符否先建立有效权限请求被拒绝
模型传入其他租户或任意网络工具否提议超出接口范围结构或允许列表检查拒绝

最小权限(least privilege)要求每个组件只得到本职工作所需的权限。只读助手不需要取消凭据;审批人需要记录决定的权限,不需要无限制读取订单;来源核验脚本需要的是输入和经过审查的资源,不是用户凭据。

审批本身也有范围。允许 north/Mira 取消版本 1 的 A-104,不等于允许取消 A-105、south 的 A-104,或后来发生变化的新版本。第 49 章的操作指纹绑定操作及参数,审批记录还绑定主体、租户和有效期。测试会拒绝变更参数、过期审批,以及可写用户冒充审批人的尝试。工具返回中一段看似真人授权的文字,不能创建这种记录。

53.3 核对字节之后,还要问谁担保了来源

Section titled “53.3 核对字节之后,还要问谁担保了来源”

例 53.2 审查后的 Skill 被改动。 作者审查来源核验包的 1.0.0 版本,把文件路径和 SHA-256 摘要保存在独立留存的清单中。SHA-256 根据文件字节计算出固定长度的摘要。文件变化后,摘要比较应当失败;这检查完整性,并不解释文件是否安全。

回归分别把已审查的 Skill、辅助脚本和本地 MCP 实现复制成临时文件。最初,三者都与已记录摘要一致;追加一行未经审查的文字后,三者都不再匹配。运行器在执行之前拒绝这些变化后的字节。指向允许目录之外的符号链接也会被拒绝,避免看似正常的路径偷偷换成未经审查的目标。

不能在比较之前,直接从待检查文件计算“预期摘要”,再与它自己比较。预期清单必须来自此前审查过的快照。本项目的常规记录运行器加载固定清单,发现不匹配就停止,不会自动更新许可。确实要更新时,应先审查,再建立新快照并重跑验证。

表 53.2 不同依赖需要不同的来源证据。

依赖本项目保留的证据尚不能证明什么
来源核验 Skill 及资产本项目来源、版本、许可、已审查文件摘要、脚本测试摘要不能证明原作者的决定安全
MCP 服务端实现本地代码摘要、只读契约、协议依据、既有测试协议兼容不等于每个服务运营方都可信
Python 依赖包实际安装版本、发行包元数据、沿用的版本锁定文件固定版本不能证明仓库、维护者或安装产物未被入侵
本地模型仓库与标签、安装清单摘要、五份核对过的内容文件、上游许可依据没有独立验证过的转换或构建证明,把量化产物与上游权重连接起来
模型运行时与服务运行时版本、API 设置、本地 CPU 观察没有生产服务安全评估,也没有服务商保留策略保证

已安装 Qwen 的清单摘要为 458ce03a21878025d072b8740d0e0e3b34abec3585fdadb0b5ab92d1e85dfb61。实际本地检查中,五份内容文件的大小和摘要都与清单一致。另一项测试把待接受摘要设为 64 个零,与固定值比较后被拒绝。我们没有破坏或执行改动后的权重,也不把权重文件放进仓库。

这些检查建立了本地文件身份和一致性,同时保留来源证明的缺口。恶意发布者可以同时提供恶意字节和匹配摘要;失陷的审查人也可能批准错误清单。SLSA 所讨论的更强构建来源验证,还需要可信构建者身份和经过验证的预期条件。本项目的无签名教学清单不声称达到任何 SLSA 等级。Skill、MCP 连接或模型能工作,只能说明能力可用,不能说明来源可信。

53.4 验证隔离,也要单独验证授权

Section titled “53.4 验证隔离,也要单独验证授权”

沙箱(sandbox)限制进程能访问或修改什么。它与“Mira 能否取消这张订单”处在不同边界。进程即使受到良好隔离,也可能滥用它能读取的记录;反过来,一项授权正确的操作也可能运行在权限过大的进程中。

例 53.3 一次真实但范围有限的进程实验。 在可选的 macOS 实验中,创建一份公开夹具文件、一个仅包含 DEMO-SECRET-ORCHID 的受保护目录,以及本机回环监听器。将同一个 Python 子进程分别直接运行、在隔离配置下运行,不连接外部地址。

表 53.3 实际观察到的进程行为。

尝试未使用隔离配置使用隔离配置
读取允许的夹具读到 public-fixture读到 public-fixture
读取指定的受保护夹具读到虚构秘密PermissionError,错误码 1
连接本地监听器连接成功PermissionError,错误码 1

该配置拒绝全部网络访问,禁止读取指定的受保护夹具目录,并把写入限制在临时工作目录和 /dev/null。一般运行时读取仍被允许。它是针对 macOS 的诊断配置,不是通用文件系统允许列表,也不是生产沙箱。更早的一版配置使 Python 无法启动;这次失败单独保留,没有被算成成功隔离。进程连正常工作都做不了,不能作为充分的正向验证。

实测建立的是记录平台上这些具体文件与网络边界。它没有证明抵抗沙箱逃逸,没有保护宿主中的每个文件,也没有测试其他操作系统。可移植的 CPU 检查另行验证工具分发和授权,不会假装重做了这项操作系统实验。

请求 → 数据
  1. 应用保存的记录宿主:north/mira
  2. 处理步骤查询条件:租户 north 且所有者 mira
  3. 模型之外A-104:处理中

south 中同名订单属于 leo,状态为已发货;只有订单 ID 不够。

代码 → 副作用
  1. 应用保存的记录已审查来源+预期摘要
  2. 处理步骤允许清单+沙箱+审批
  3. 应用保存的记录范围内的副作用+审计回执

恶意包若未改动,也能通过哈希检查。隔离进程仍可能滥用已经拿到的凭据。

图 53.1:执行前分别检查四种条件。 原创教学图示 · CC BY-SA 4.0

图 53.1 把各项控制分开:宿主身份确定权限,审批允许具体写入,来源核验选择已审查字节,隔离限制进程触及范围,审计记录保留决定和副作用。去掉其中一项,可能留下另一条漏洞路径。审计本身不能阻止未授权操作,但没有审计,我们也可能无法核对结果不明的请求。

现在,助手具有一个只开放一处模型选择的有限 RAG 工作流、可复用的来源核验 Skill、持久保存的模拟操作恢复、分层评测,以及治理和安全记录。最初的 Keyword、BoW Count、TF-IDF 对照保持完整,此前 Mini GPT 的验收决定也保持不变。另用 Qwen 做本地实验,并没有改变那个模型的验收状态。

表 53.4 KA-5 至 KA-8 的交接证据。

里程碑可观察结果仍须承担的责任
KA-5一次受限选择;现行 750 元;资料为空或预算耗尽时停止新任务需要自己的证据和状态转移契约
KA-6Skill 加载轨迹;拒批时副作用为零;丢失回执后恢复仍只有一次副作用;回放没有修改远程服务必须提供自己的幂等和核对契约
KA-7两系统均为 3/4,但首个失败层不同;四个差旅人工案例一致;公开人工样本一致率 0/4自动 Judge 放行仍关闭;需要更多独立领域复核
KA-8字段最小化、四处删除、到期处理、本地攻击拦截、租户与审批测试、变化字节拒绝,以及有限的操作系统实验真实身份、运营保留、备份、来源权利、事件响应和安全部署仍需人及系统落实

项目是本地教学系统,不是运营中的服务。核心 Notebook 在 CPU 上使用小型夹具和已保存的可选模型记录。模型调用、人工判断、手工给定的例子和实际宿主执行始终分开。小型安全回归可以防止已知错误重新出现,不能证明不存在未知攻击。

53.6 本部分检查点:不靠框架名字解释控制

Section titled “53.6 本部分检查点:不靠框架名字解释控制”

先试着回答下面六个迁移问题,再看后面的解答。它们串起整个部分,不要求背组件名称。

  1. 资料为空,模型却要求一直搜索。哪个组件负责停止请求?应保留什么状态?
  2. 新的来源核验包名字相同,字节却变了。运行之前必须做什么?
  3. 取消操作的回复丢失。什么证据能区分“没执行”和“执行了但没报告”?为什么换一个新键危险?
  4. 两个系统都通过 3/4 请求,还需要什么记录才能决定修复位置?
  5. 引用来源真实却写错了。谁处理实质争议?之后应检查哪些现行与历史问题?
  6. 沙箱中的工具拿到有效 north 凭据,却收到模型传入的 south 租户。哪些控制仍然必须拒绝该请求?

权限来自经过验证的主体和范围明确的操作,不来自有说服力的文字,也不来自订单 ID 本身。已审查摘要能发现相对于固定清单的变化,却不能创造可信来源。沙箱限制可触及范围,却不授予操作权限。KA-8 把这些控制与审计、剩余责任放在一起。第 54 章将继续讨论,怎样在保留这些边界的前提下,把本地设计变成可用服务。

  1. north 和 south 都有 A-104,缓存却只把订单 ID 当作键。可能怎样泄漏?使用缓存结果时,还需要哪些身份和版本信息?
  2. 凭据允许查询和取消,但目标是另一个服务。即使取消已获批,也应执行吗?指出首先失败的条件。
  3. 审批人批准的是版本 1,执行前订单已变为版本 2。应如何处理?为什么“订单 ID 没变”不足以维持原审批?
  4. 改动后的脚本附有下载页面上新发布且匹配的校验值。哪项完整性检查可以通过?哪个信任问题仍未解决?
  5. 沙箱拦住了虚构秘密,却允许访问含两个租户记录的数据库。表 53.3 已经证明租户隔离了吗?提出另行必需的测试。
  1. south 缓存的 shipped 可能被返回给 north/Mira。应按可信租户及适当的主体、权限范围划分缓存访问,并在取出时检查当前权限。键或校验条件还要包含对象、来源版本及相关请求条件,避免复用过期或范围不同的结果。命中缓存不能豁免访问检查。
  2. 不应执行。目标服务验证先失败,请求的权限范围和审批都不能授权当前服务使用这份凭据。审批不会修复无效凭据,必须先通过宿主认证路径取得适用于该服务的有效权限。
  3. 拒绝过期版本,或为已经变化的操作背景请求重新审查。获批的对象状态已经改变,版本检查防止旧审批悄悄覆盖新状态。
  4. 下载字节可能与同一页面的校验值一致,但这不能说明谁控制页面、脚本是否经过审查、构建是否可信。应与另行信任的预期比较,并在更新预期之前审查变化。
  5. 没有。进程可能滥用它本来就能读取的数据。应在应用与存储边界另测两租户重复 ID、经过认证的所有者检查、跨租户参数拒绝和缓存隔离。操作系统实验回答的是另一个问题。

本部分检查点解答。

  1. 宿主的确定性转移和预算规则负责停止或暂不作答。本工作流在资料为空时进入 ABSTAINED,不调用模型。应保留原因和观察到的资料状态,模型要求不能扩展状态图或预算。
  2. 拒绝不匹配版本,审查来源、指令、脚本、资产和依赖,再有意识地批准新版本与清单,并运行测试。熟悉的名字不会授权已经变化的行为。
  3. 核对绑定租户、主体、操作和幂等键的持久回执,再核对当前状态。本例中,副作用与回执在同一数据库一起提交。新键可能代表新操作,从而重复远程副作用;只回放已有事件则不会执行操作。
  4. 请求级结果、检索产物、拼好的上下文、提议答案及依据、工具轨迹,能显示最早观察到的失败。相同总分丢失了这些位置信息。修复后还要重跑受影响及后续检查。
  5. 制度负责人调查来源是否正确,服务负责人据此更新来源、索引和缓存。在相应日期下保留现行 750 元与有效历史 600 元。仅核对引用文字,无法解决实质争议。
  6. 拒绝额外租户参数,并让存储和缓存访问始终限定于经过认证的租户及获准访问的所有者。同时检查目标服务、有效期和操作范围。隔离不会改变身份,也不会授予跨租户权限。
  • MCP 授权规范,2026-07-28与官方安全实践,权限范围、目标服务、混淆代理及令牌透传相关部分:支持按服务限定权限并独立检查授权;本地访问主体不是 OAuth 实现。
  • Agent Skills 规范,资源与渐进加载部分:包装可复用指令,并不建立可信来源或执行权限。
  • OWASP GenAI 2025:LLM03 供应链,缓解措施第 5—9 项:支持为依赖和模型资产建立清单、许可、完整性检查与持续复查。
  • SLSA 1.2:验证产物,验证步骤与建立预期部分:可信构建来源所需的证据不止匹配摘要,本项目不声称通过 SLSA 认证。