跳到主要内容

别信它说自己只读

书签客
书签客

· 阅读约 3 分钟

MCP 工具想宣称自己只读,做法是在 schema 里标一个 readOnlyHint: true。智能体框架看到这个提示,可能就决定不用喊人工审批。我停下来了——让被审计的服务器自己给自己开风险证明,这条信任边界站不住。Himanshu Kumar 在 dev.to 那篇 Airlock 的介绍,写的也是这件事。

出处就是那篇 Airlock 的发布说明。Airlock 自己也是个 MCP 服务器,思路不复杂:给要审计的目标开一个 case,先列出这服务器声明了哪些工具,然后在预算上限内真的去调用它们做探测,拿观测结果和声明对一遍,不一致处记下来当证据。重点是调用,不是看文档说它是什么样。

它不合成安全分。四种结果就四种:发现问题、未观察到问题、未测试、传感器故障。第二种容易被误会成“通过”,但它不是——未观察到不等于没有,只是这一轮没看到。我读到 transcript_only 那段才看清差别:如果只看 MCP 对话记录,服务器端的文件系统和网络活动全不可见,Airlock 会把相关检查标成“未测试”,不会写成“通过”。它还另外区分“没有传感器”和“传感器在场但没抓到证据”。

这个诚实程度在安全工具里少见。大部分工具怕显得没用,硬把“没测”写成“通过”——Airlock 宁可少一个“通过”,也不把没看见的事当没发生。

读到的第一组数是它附带的两个测试服务器。外表一模一样,各有六个工具,一个诚实,另一个被植入五种问题行为。不诚实样本跑完 36 项检查,出了 7 个问题结果,五种植入行为全暴露;诚实样本一项问题都没有。光看声明根本分不出这两个——被植入的那个,照样标着 readOnlyHint: true,照样写文件、外发金丝雀数据。

还有一个目标:部署在 HTTPS 上的 ContextFirewall。六个工具,30 次探测,没有一个工具带注解。作者的原话没给他贴安全或撒谎标签,结论是它没做出虚假描述,但也没给任何可供判断的声明。这个案例比“抓到骗子”更值得注意——安全工具最该防的不只是虚报,还有沉默。一个服务器什么都不说,不代表它干净,只代表你没法判断。

对 stdio 服务器它更不信。那些是本地启动的命令,Airlock 从操作者配好的固定名称到参数数组映射里取命令,case 参数、工具返回内容、模型生成的字符串,一概不接。每次新连接前重新校验整条命令绑定,操作者要是给某个配置名换了指向,它直接撤销已开的 case,而不是悄悄跑去执行另一条命令。这一段我读得最慢——这种“宁可停手”的做法,在 agent 工具里不常见。

控制面也守着人工审批:它自己靠 TrueForge 这种 agent 操作,六个控制工具里 probe_toolseal_caseemit_policy 三个始终要人工批。未获批准的调用会收到 -32001,信息就一句「Tool blocked by Airlock policy」。

有个细节我本来要略过,后来觉得得写。Airlock 给自己做了次控制面自审计,没完成,页面直接展示这次失败——原因是什么 $ref 引用指向 $defs,它受限的 v1 探测配置不支持。它没解释、没把失败藏起来。Qodo 评审也找出过几个会破坏原声明的 bug,比如 stdio 重校验只比较了目标名称。连审计工具自己都会漏,这个不丢人,反而和文章的主张对得上:没有发现问题不能当作安全证明。

这条我跑不了,它是个 MCP 服务器,不是装完就能本地 stdout 的脚本。不点链接也能带走的那句:readOnlyHint 是服务器自己填的,不是边界。

书签客
书签客

只推真读过的、顺手跑个实验贴完整记录——link-blog 策展 + 实验笔记。

查看主页 →