Agent密钥运行时落地实录:杜绝明文泄漏的安全工程实战

文章目录

    • 前言
    • 一、先把三个词掰扯明白
      • [1. 凭据(credential)](#1. 凭据(credential))
      • [2. Secret Reference(秘密引用)](#2. Secret Reference(秘密引用))
      • [3. 金丝雀(canary)](#3. 金丝雀(canary))
    • 二、设计:先画边界表,再写代码
    • 三、落地:九个阶段,一个都不能跳
      • [1. Phase 0 · 基线与表征测试](#1. Phase 0 · 基线与表征测试)
      • [2. Phase 1 · Secret Reference 与加密](#2. Phase 1 · Secret Reference 与加密)
      • [3. Phase 2--3 · 迁移与存储](#3. Phase 2–3 · 迁移与存储)
      • [4. Phase 4 · 信封与授权门](#4. Phase 4 · 信封与授权门)
      • [5. Phase 5 · 解密与出口](#5. Phase 5 · 解密与出口)
      • [6. Phase 6--8 · 审计适配、端到端测试、全仓回归](#6. Phase 6–8 · 审计适配、端到端测试、全仓回归)
      • [7. 组织设计:8 个 Agent,1 个主控](#7. 组织设计:8 个 Agent,1 个主控)
    • 四、收口战役:三件比代码本身更值钱的事
      • [1. 事件一:P0 级漏洞------RLS 旁路](#1. 事件一:P0 级漏洞——RLS 旁路)
      • [2. 事件二:结果文件里的矛盾被揪出](#2. 事件二:结果文件里的矛盾被揪出)
      • [3. 事件三:双独立安全审计](#3. 事件三:双独立安全审计)
    • 五、现状:离生产只差一道"人工"门
    • 小结

P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看, 传送门https://blog.csdn.net/H1727548

前言

现在做 Agent 的人,十有八九都被 API key 折磨过。你高高兴兴给 Agent 接了个外部服务,转头它就把你的 key 原封不动复述进了对话里。那一刻你不是在写代码,你是在养一只会背密码的鹦鹉。

后来我盯上一套刚落地的 Secret / Credential Runtime,专门治这种病。核心诉求一句话:Agent 可以带着凭据去调用外部服务,但凭据的明文,模型看不到、日志看不到、数据库看不到,连它亲妈都看不到。

复制代码
状态:IMPLEMENTED / LOCAL_MVP_PASS / LOCAL_COMPLETE_PRODUCTION_WAITING_HUMAN
(2026-09-17 收口更新:Phase 0--8 全部完成,本地自动化门禁全绿;
 P0 漏洞 ADR-025 §10-1 已由迁移 000214 收口;ADR-025 已 ACCEPTED;
 push 已完成(82b2e3f6 已至 origin/main 与 github/main);
 生产部署验收是唯一剩余人工门,生产启用继续禁止。)

今天我在仓库里把它的三层测试真跑了一遍:领域层的拒绝矩阵,42 种"看起来差不多"的变体全部被拒 ------大写 scheme、大写 UUID、带 port、藏 userinfo、塞 query、无连字符,每个变体一个子测试;应用层信封剥离 6 测全绿;执行层 e2e 4 测全绿,其中最讲究的叫 RealResolverCanaryHeaderExactlyOnce:用真实解密链让金丝雀明文流到 mock 外部服务,断言 header 里恰好出现一次,所有内部出口零次。收口文件里那个最终数字也直白得可爱:plaintext_leak_total: 0。

顺便交代个反面教材:我本想在 /tmp 写独立 demo 调解析器,被 Go 的 internal 包规则拦住了------仓库刻意不对外暴露内部包,这本身就是一道安全边界。所以本篇实测全是跑仓库自带测试,这是诚实的选择,不是妥协的借口。行,说人话开整。

一、先把三个词掰扯明白

1. 凭据(credential)

凭据就是让外部服务认你身份的暗号:API key、访问令牌之类。它跟普通配置唯一的区别是:普通配置泄漏最多算社死,凭据泄漏直接算公司没。

泄漏渠道多到你不敢数:模型把它复述进对话、写进日志、存进数据库、被工具结果带回来、被 trace 顺手采走......这套运行时要做的,是把这些渠道全部焊死。不是堵一条,是全堵。堵一条叫打补丁,全堵才叫安全。

2. Secret Reference(秘密引用)

明文不能到处跑,那就给它发个代号。格式定死:

复制代码
secret://vault/{小写UUID}

注意这个 UUID 是随机标识,不含任何信息量------你猜到天荒地老,也拿不到明文。设计原则就一句话:模型和一切记录里只允许出现代号,明文只允许出现在三个受控边界(写入加密时、解密回调内、发往外部服务的 header 里)。

说人话就是:明文这辈子只出三次门,比社恐还克制。社恐出门还要纠结半小时,明文出门是被人拿枪顶着的。

3. 金丝雀(canary)

矿工下井前带只鸟,鸟死了说明有毒气。我们这儿更狠,我们带的鸟会数数。

测试里把一个已知的假明文当真凭据跑完整链路,然后数它出现的次数:模型上下文 0 次、消息 0 次、日志 0 次、数据库 0 次、mock 外部服务的 header 恰好 1 次。次数对上了,才证明管道没漏。

别笑,这个"数鸟"方案,比市面上 90% 的所谓密钥管理方案都靠谱。人家是"我们很安全,请相信我们",我们是"我们很安全,请数数"。数得出来的安全,才叫安全。

二、设计:先画边界表,再写代码

1. 一张"明文边界表"定了全局

这套设计最值得抄的不是代码,是一张明文边界表:把系统里每个位置逐一行刑------哪里允许代号、哪里连代号都别想、哪里明文可以短暂现身。

具体规则就三条主线:

(1)模型侧

LLM 上下文、提示词、Agent 状态、工具调用参数、对话记忆摘要、工具结果、错误信息、SSE 流------代号随便放,明文想都别想。模型是泄密重灾区,它一开口,你的 key 就成了段子素材。

(2)记录侧

日志、追踪、审计、事件表------同上,只有代号。数据库更狠,连代号都不存,只存加密密文。管理 API 要啥啥没有,只有元数据,比渣男的前任还神秘。

(3)受控出口

受控写入→加密、解密回调、header 注入------明文可以来,但限时三秒,办完就走。普通工具业务逻辑?禁止。外部服务?那必须允许,不然这密钥是拿来镇宅的吗。

这张表看着朴素,执行力全靠十条安全不变量(SEC-01 到 SEC-10),每条都配了机械证明方式。比如 SEC-01"LLM 上下文出现次数为 0",证明方式是 FakeLLM 捕获请求后精确扫描。不变量写成可执行的断言,而不是写成愿望------这是整个计划最硬的一条方法论。愿望这东西,写代码的人谁没几个,最后全变成了事故。

2. 主落点为什么选 Tool Runtime

候选有四个:Agent 层(太早,会污染模型)、Tool Gate(管不住真正的网络发送)、HTTP 层(完不成授权)、独立凭据微服务(MVP 就引入跨进程明文,得不偿失)。

最终答案是 Tool Runtime ------hybridToolBroker.Invoke 这一层。为什么?因为它既是所有出站请求的必经之路,又能做授权判定(主体/工具/秘密三方匹配)。改动最小、控制最全,属于"你全都要,但你不用多付钱"。

三、落地:九个阶段,一个都不能跳

计划把落地切成 9 个阶段,每阶段带验收 ID 和 STOP 条件。下面是我实际走过的台阶,每一步的产物在仓库里都能查到。

1. Phase 0 · 基线与表征测试

先不写新功能,把现有行为用测试锁死:"无凭据的调用走原路径,逐字节不变"。这叫表征测试(characterization),是后面一切改动的安全网。改完代码跑一下它,绿灯=旧世界没被动过。

听起来平平无奇?这是全项目性价比最高的一步。没有它,后面每次改动都是在悬崖边蹦迪,还觉得蹦得挺好看。

2. Phase 1 · Secret Reference 与加密

写解析器,就是我跑的那个 42 拒绝矩阵。有个细节值得单拎出来说:scheme 检查必须在标准库 url.Parse 之前做 ,因为 url.Parse 会把 SECRET:// 静默小写成合法 scheme,绕过你的大小写校验。

go 复制代码
// 别学我,先被坑过
u, _ := url.Parse("SECRET://vault/xxx")
// url.Parse 会把 scheme 静默小写:SECRET 变 secret,
// 你的大写校验从此形同虚设
if !strings.EqualFold(u.Scheme, "secret") {
    return errors.New("非法 scheme")
}

这个坑特别阴。你写好了大写校验,url.Parse 默默把它小写了,然后你的校验代码笑呵呵地说"合法"。就像你妈进你房间不敲门,还顺手把你的空调关了,等你反应过来,感冒已经安排上了。

加密复用现有的 AES-256-GCM,只加了个 AAD 绑定:把租户/秘密/版本三要素绑进密文的认证范围。密文被挪到别的租户或别的版本下,直接解不开。相当于钥匙上刻了房号,串门也开不了锁。

3. Phase 2--3 · 迁移与存储

五张新表(秘密、版本、授权、出口策略......)走 000211 迁移,全部带行级安全(RLS);存储层加密落库,管理 API 只回元数据------明文?没有。密文?也没有。你问啥都只有元数据,比渣男的前任还神秘。

4. Phase 4 · 信封与授权门

模型发起工具调用时多带一个保留字段 credentials,Gate 在业务校验之前把它剥掉------下游工具只看到业务参数,密钥像被信封包着,拆都不让你拆。

授权判定是一条 13 个 AND 的链:租户精确匹配、授权在有效期、主体匹配、工具名匹配、秘密状态正常、版本存在、出口策略激活、host/path/method 三元组精确命中......任何一环失败即拒绝。注意这个细节:任何一环查询出错,也拒绝。fail-closed:宁可误杀一千,不可放过一个。像我查对象手机,查不到也当有事处理,安全永远优先于体面。

5. Phase 5 · 解密与出口

真正发请求的地方,顺序是铁的:出口预检 → 参数复核 → 注入方式复核 → 先写"使用意图"审计(写失败就不解密、不发网) → 才在解密回调里注入 header 发出。

响应只回状态码------远端返回的 body/header 一律不进工具结果。为什么?防"远端回显"把你的 key 念回来。你打个电话,对方一开口念你密码,你还没反应过来,话已经传出去了。所以干脆不让你说话,只让你"嗯"一声。

重定向全拒、私网 IP 全拒。为什么拒私网?因为你的 key 万一被拐到内网溜一圈,回来就不是你的 key 了。跟猫似的,放出去浪一圈,回来就不认识你了。

6. Phase 6--8 · 审计适配、端到端测试、全仓回归

包括金丝雀链路、泄漏矩阵、RLS 越权测试、并发测试,最后全仓 build/lint/DDD/测试/race 全绿。这块没什么好讲的,属于工程领域的"把地扫干净"。

但你知道吗,扫干净地的人,通常也是第一个发现角落有屎的人。扫地是手段,找屎才是目的。

7. 组织设计:8 个 Agent,1 个主控

计划还规定了最多 8 个并行 Agent、1 个主控,主控是唯一的提交者和验收者;每个工人只改自己独占的路径;任何安全门出现金丝雀泄漏立即全局叫停。

把"多 Agent 协作"本身当成需要设计的系统------多 Agent 干活最怕的不是活多,是活乱。8 个人同时改同一个文件,那不叫协作,那叫密室逃脱,而且逃脱的是你的代码。

四、收口战役:三件比代码本身更值钱的事

按计划写完只是及格线。收口阶段发生了三件事,每一件都比代码本身更有参考价值。

1. 事件一:P0 级漏洞------RLS 旁路

000211 的五张表用了"平台管理员可旁路"的策略,判断条件是一个数据库会话变量(GUC)------但 app_user 自己就能设置这个变量。

翻译成人话:普通人给自己贴个"我是管理员"的标签,就能跨租户读写删。这不是漏洞,这是给门卫发了一把万能钥匙,钥匙上还写着"我是门卫"。

机械负例测试在主分支上跑出 28/40 格红------40 个越权尝试成功 28 个。这个数字你敢信?这不是漏洞,这是开卷考试,而且及格线 70 分,你考了 70。

修复(迁移 000214)直接删掉五张表的旁路策略,负例重跑 44 PASS / 0 FAIL ,且 down 迁移能精确还原、往返闭环。注意:这个漏洞没有任何已知利用,是被自己人用负例测试主动挖出来的。自己给自己扫雷,扫完还留了张"此处有雷"的纸条。

2. 事件二:结果文件里的矛盾被揪出

收口证据文件曾同时写着 all_pass: true 和某项 PENDING,人工门状态还停留在旧时点------多轮追加字段没回填早期状态所致。

处理方式是修正矛盾并以追加更正块留痕,不静默重写。证据文件自己必须自洽,证明体系本身也要被审计------这是"审计门的门",同一套纪律。

这事特别丢人,但也特别提气:写报告的和写代码的是同一拨人,结果报告自己先打起来了。好在发现得早------如果这份文件带着矛盾进生产,审计的时候就不是丢人,是丢人 plus。

3. 事件三:双独立安全审计

收口后跑了两轮独立评估(实现质量 + 终态治理),结果 0 严重 / 0 高危。比我的体检报告还干净。

2 个中危都是 fail-closed 方向(配置陷阱而非越权),连同 5 个低危一起登记进遗留清单等批次修复。最有意思的一条 MEDIUM:出口路径前缀匹配的归一化逻辑在 Go 侧和 SQL 侧不一致,管理员配 "/" 或 "/api/" 时规则会静默失效------方向是不放行(安全),但属于坑管理员的配置陷阱。

审计报告敢把"我们的两处实现不一致"写进表格,这比 0 严重更让人放心。敢自曝家丑的团队,通常家里真没丑事。

五、现状:离生产只差一道"人工"门

  • 代码 :合并链 ed4ae0d2 已推送,82b2e3f6 在 origin/main 与 github/main;当前 HEAD 门禁全量复跑绿(build/lint/DDD/单测/集成/开放 API 校验/vet/eval 快照)。
  • 架构决策 ADR-025:负责人 2026-09-17 验收,ACCEPTED。
  • 生产启用:NO-GO,等人工 。剩余三件事全是人的事:部署前安全验收记录、负责人单独授权、生产环境配置 DF_SECRET_KEY。

设计上这个"没配置"状态是安全的------凭据能力整体禁用、fail-closed,不是事故态。代码说"我已经准备好了",人类说"我再想想"。这一想,就是安全的全部意义。

还有两项"工作树字面不绿"如实登记:inventory 有 2 个非本战役文件未登记(别的战役产物);共享工作树的 gitleaks 命中全部来自 gitignored 的本地文件------门禁口径固定为干净树扫描(git archive HEAD = 0 findings),两种口径不混用。

如实分层:本篇实测覆盖领域层/应用层/执行层与 e2e 金丝雀,全部本机真跑;全量集成测试(257 包)我未复跑------那是收口战役的证据(连续两次全绿),我引用而不冒领。生产部署验收未发生,生产启用仍是禁止状态。

小结

1. 先画边界表,再写代码。"哪个位置允许明文"一行一行定死,每行配机械证明------设计阶段的这张表,比实现阶段的任何技巧都值钱。42 个拒绝变体、10 条不变量、金丝雀计数,全是把"禁止"翻译成"可断言"的动作。

**2. MVP 的克制是安全设计的一部分。**只支持 HTTPS 443、只支持一个凭据、重定向全拒、远端 body 一律不回、MCP 一律不接------每一条"不做"都消掉一类攻击面。计划里"禁止范围"一节与"交付范围"同等醒目,拒绝诱惑也是一种能力。

**3. 落地 ≠ 写完代码。**这份"落地"包含:表征测试锁旧世界 → 负例测试挖出自己 28/40 的 RLS 旁路 → 修复并往返验证 → 证据文件矛盾被自查修正 → 双独立审计 + 遗留登记 → 人工门清单。

最后那道"生产仍禁用"的门保持关着,本身就是这套安全工程的收笔。门关着,才是安全的。门开着,那是事故。

P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,传送门https://blog.csdn.net/H1727548

相关推荐
王中阳Go1 小时前
面试官问"你怎么证明它有效",200个转AI的后端没几个答得上来
人工智能·后端·面试
LucianaiB1 小时前
华为云码道 AI 编程实战:我用 CodeArts 智能体打造了一款 HarmonyOS 游戏化专注应用「智办 ZhiBan」
人工智能·ai·华为云·harmonyos·codearts·材料
宋哥转AI1 小时前
AgentScope Java 实战 03:知识与工具层——给 Agent 装上手和书架
人工智能·agent·ai编程
海宇AI1 小时前
零信任架构实战:基于海宇学历核验版构建自动化高并发资信评估网关
人工智能·微服务·架构·自动化
阳明山水1 小时前
因果嵌入与流水线范式的本质差异
人工智能·深度学习·算法·机器学习·架构
代数狂人1 小时前
机器学习数学基础──第 2 章 函数 机器学习的积木
人工智能·机器学习
用户777636100261 小时前
类Jev项目Kev从入门到实战(2):Kev 的架构:一次前向,多问题隔离作答
人工智能
rhett. li1 小时前
用 AI 写 C++ 桌面 UI:TRAE + nim_duilib 实战指南
c++·人工智能·ui
yaoyuxianggnn1 小时前
我的 Agent 一晚上烧了 500 块,于是我把它的死循环拆成了四种形态
人工智能