技术 · 熟练
权限写在提示词里,等于没写
把「允不允许」从一句话换成一次判定
提示词层的安全是「概率上的不太可能」,策略层才是「结构上的不可能」。把权限分级写进提示词、让模型自己判断哪些动作要停下,在演示里永远成立;但一份可查的对照表里,自适应攻击对四个主流模型的成功率是 100%。这一页讲的是把判定搬一层:由策略引擎决定 allow 还是 deny(用配置文件写,不用自然语言写)· 给每个 agent 一个可核的身份 · 每次判定留一条防篡改记录 · 策略自身坏掉时默认拒绝而不是放行。判据一句:如果「拦住」这件事依赖模型听话,那它就不是一道闸门。
方法 · 一图看懂
1
把判定点移到调用边界 — 每一次工具调用 / 消息发送 / 委派都要过一道判定,而不是靠模型自己「记得」规则
2
判定写成配置,不写成自然语言 — allow / deny 用 YAML / OPA / Cedar 这类可评审、可版本管理的策略表达
3
身份与凭据要可核 — 每个 agent 一个可验证的身份(SPIFFE / DID 这类),凭据短期、可失效、可吊销
4
让拒绝成为默认,让记录成为副产品 — 策略坏掉 / 读不到时判拒(fail-closed);每次判定自动落一条不可改的审计记录
原理 · 为什么有效
两者的差别不在严格程度,而在「谁说的算」。
提示词层的结构是:规则 → 模型理解 → 模型遵守。中间那一步是不可验证的 —— 你无法证明模型真的按规则判了,你只能观察它的输出像不像守规矩。当输入里出现对抗性内容(别处的文档、抓回来的网页、别人写进工具返回值里的字),这一步就是可攻击面;而一旦命中,没有一道闸门被绕过 —— 因为本来就没有闸门。
策略层把中间那一步换成确定性判定:调用 → 判定 → 允许或拒绝,判定不看模型的心情,只看配置写的规则。此时「拒绝了」这件事有确定的原因,可以被复现、被审计、被版本管理;而策略自己出问题时(配置读不到 / 规则写坏了),默认值是拒绝而不是放行 —— 这一点决定了失败时的方向。
代价也要说清:它需要有人维护那份策略,而策略是会被绕过的(写得太宽的规则同样等于没有)。所以判据不是「装了策略引擎就安全」,而是「同一条策略在不同机器上跑出同一种结果」。
适用场景
适合:给了 agent 真实权限的人(能读写文件 / 能发消息 / 能下单 / 能碰生产环境)· 多个 agent 共用一套接入(MCP 服务器 / 内部工具)· 团队里要把权限从「各自配置」收拢到统一治理 · 需要留痕以备审计的场合。
不适合:只在自己一台机器上、agent 只读不写的人(先做好最小权限即可,别上治理面)· 还没到「给权限」阶段的实验(先用提示词那一版就够)· 把「上了策略引擎」当成验收终点的人 —— 那正是这一页要纠的读法。
自测 · 5 问
我说得出「拦住」这件事由谁判定吗?是模型,还是一段别人看得懂的配置
我的 agent 有几个身份?它用的凭据是长期的还是可失效的
策略读不到 / 写坏时会怎样 —— 拒绝,还是放行?
同一个策略在不同机器上跑,结果一致吗?我有过对照吗
每一次「拒绝」找得到原因吗?翻得出一行可核的记录吗
怎么上手 · 验证步骤
照这个顺序做:① 先把现有权限列成一张表(谁能调什么 / 读什么 / 发什么),逐条问「去掉这一条,任务还做不做得了」;② 把最该拦的那几条写成一条策略(先只写 3~5 条,别一次铺满);③ 给这条策略造一个反例 —— 故意让它拦一个本该拦的调用,确认真的被拒;④ 看记录,确认这次拒绝留下了一条可查的痕迹。做完这四步再谈铺开。
做完自己核对三件:① 我有一条策略,且它拦下过一个真的调用(不是只在文档里存在)② 把策略文件临时挪走,系统是拒绝而不是放行 ③ 每一次拒绝都能指到「哪条规则 · 什么时候 · 哪个 agent」。只要有一条答不上来,这一层就还没建成。