作者设计模型替换攻击测试后发现:攻击被正确拦截,但原因是测试场景本身有漏洞,而非防御真正生效。
攻击生效了。我向自己的控制平面提交了一次 model-swap 运行,期待返回 403,却得到了 201——准入。
大约有一个小时,我以为自己找到了 admission gate 的漏洞。这个 gate 位于 HivePlane 中——我构建的控制平面,所有请求在无签名认证的情况下无法触及生产环境——我即将度过非常糟糕的一周。然后我仔细看了我的场景实际做了什么,得出了令人不安的结论:gate 是对的。我的安全测试是错的。
如果你曾经见过安全测试通过却感到怀疑而非欣慰,这个故事就是讲给你的。
我要证明的攻击:一个在模型 A 上获得认证的 agent 试图在模型 B 上运行。因此我注册了 model-swap-agent,用一个不同的模型身份提交了运行,然后等待拦截。
gate 看到的却是:一个从未经过任何认证的工作负载。而 model-binding gate 不会比较"期望",它比较的是运行身份与认证所绑定模型的一致性。没有认证,没有 attestation,没什么可比较的。该运行被合法准入。
两道 gate 保护模型身份,而我的场景落在了两者之间:
我把一个能触发第一个 gate 的工作负载,直接送入了第二个 gate 的领域。这相当于安全测试中检查后门锁的方式——从前门走进去然后宣称房子不安全。
tip:在安全测试中,应该警惕的失败模式不是红色结果。而是对一个你从未实际验证过的主张给出绿色通过。我的测试自信地错了。
修复在于场景,而非系统。真正的攻击——"在 A 上认证,在 B 上运行"——有两步:
先用 --model-identity omlx/qwen3-4b-instruct-2507/4bit 认证 model-swap-agent,将 attestation 绑定到该服务身份。
然后用不同身份提交生产运行:openai/gpt-4o/2024-08-06。
403。拒绝记录同时包含认证身份和被替换的身份,gate 被两个单元测试固定——test_model_swap_is_blocked_at_admission 和 test_refused_on_model_swap——所以场景不会再次悄然漂移。
一个诱人的替代设计——"运行的模型必须等于 manifest 声明的模型"——会让我的原始测试通过。但那样同样是错的。
现场测试是合法地在本地模型上运行一个 manifest 声明为 openai/gpt-4o 的工作负载,认证时用 --model-identity omlx/...。Manifest 声明的身份是意图;attestation 才是证据。一个 manifest-binding gate 会拦截那种合法的替换——正是使本地优先操作成为可能的灵活性——而 attestation-binding gate 只拦截真正重要的:运行偏离了工作负载实际认证的模型。
运行时的身份也不是取自调用者的说法。Provider 从实际推理中报告模型,通过配置的别名映射到规范身份,不匹配时会阻止运行并记录安全事件。自报的身份永远不会被信任。
一周后,Docker 测试报告记录了我所犯错误的镜像——这次是测试种子犯的错,而非测试作者:
在 UI 中硬编码 gpt-4o 使运行正确触发了 model-swap gate,因为 repo-agent 已针对本地模型完成认证。
播种的 UI 数据在认证将工作负载绑定到本地身份后没有更新——于是 gate 拒绝了它。当你的负向控制意外触发时,那就是控制正常工作的证明。
测试攻击本身,而不是攻击的邻居。"在 A 上认证,在 B 上运行"包含一个认证步骤。跳过它以节省时间测试的完全是一个不同的命题——并且本可以在一个未经验证的主张上打上绿色勾号。
把身份绑定到证据,而非声明。Manifest 说明 agent 想要成为什么;attestation 说明它证明了什么。Gate 应该读取第二份文件。
一个错误的安全测试比没有安全测试更糟糕。没有测试会招致审视。一个错误但绿色的测试会制造虚假信心——而反驳之所以存在,只因为我读了 201 而不是假设 gate 不会失败。
阅读拒绝。201 不是在撒谎。它精确地告诉我:在没有 attestation 的情况下,不存在所谓的替换。系统的行为比我的场景更像一份规范。
别名表(HIVEPLANE_MODEL__MODEL_ALIASES)是按部署的配置;配置错误的别名是 gate 无法捕获的部署错误。
身份绑定在 admission 时阻止替换——provider 在 admission 后提供不同模型是 drift detector 的职责,将在下一版本发布。
本周期认证了一个身份;多模型替换矩阵(声明云端、认证本地、运行本地)是 cloud-profile run 的工作。
S3 场景的事后分析,以及修正后的 certify-then-swap 攻击
播种运行事件,gate 自主触发
发布日取证——我 22 MB 的包里面的 35 MB 文件,以及那个并非如此的可 tamper-evident log。
最后一个因为错误原因而变绿的测试是什么——你花了多久才注意到?