ai-安全
你的 AI 如何在从未看见你的 Stripe 密钥的情况下使用它
这套运行时密钥注入模式让 lodos 能用你的 Stripe 密钥向客户收费,而密钥从不进入 AI 模型的上下文。
每一款「AI 智能体」产品都在做一笔让人不安的交易:要完成有用的工作,智能体就需要你的凭据。要向客户收费,它需要你的 Stripe 密钥。要部署,它需要你的云端令牌。常见的做法是把这些密钥交给模型,然后祈祷不会泄露。
我们不想靠祈祷。所以 lodos 的构建方式让泄露在结构上不可能发生,而且一旦有人破坏了这一点,构建就会失败。
把密钥交给模型的问题所在
一旦明文密钥进入语言模型的上下文,你就失去了对它的控制。它可能出现在对话记录里、出现在某次工具调用中、出现在日志里、出现在模型稍后写的某段摘要里,或者出现在对一个措辞巧妙的追问的回答中。提示注入(藏在网页、邮件或智能体读取的文档里的一条恶意指令)会把这种潜在风险变成一条真实的外泄通道。
诚实的结论是:**模型一开始就根本不应该看见密钥。**其余一切都只是在一个设计缺陷之上做缓解。
引用、注入、审计
lodos 通过三个阶段处理每一个凭据。
1. 引用
你的 AI 从不直接处理一个值,它处理的是一个占位符:
{{secrets.stripe.sk_live}}
这个引用就是模型所能看到的全部,无论是在对话中、在工作流中,还是在任务中。它可以推理如何使用 Stripe 密钥,却从不真正持有它。
2. 注入
当某个任务真的需要这个凭据时,lodos 会在本地解密它,并把它作为环境变量传给一个隔离的子进程,这一切都在模型的视野之外,且在任务结束的那一刻就消失。
# The agent asked to run this; lodos resolved the reference and
# injected the real value into the subprocess environment only.
STRIPE_API_KEY={{secrets.stripe.sk_live}} ./charge-customer.sh
模型看到的是左边那一行。子进程拿到的是右边那个值。两者在对话记录里从不相遇。
3. 审计
每一次访问都会追加到一条可防篡改的 HMAC 链上。日志记录的是某个密钥被使用过这件事,而绝不记录是哪个值。字段路径是不透明的,任何试图修改历史的行为都会让这条链断裂。
我们为什么不只是信任一条策略
写一句「这个 AI 被配置为不会泄露密钥」然后就此打住,是很容易的。我们认为,没有强制执行的话这句话一文不值,所以我们把这条保证内建进了发布流程。
每次发布都会运行一次构建期不变式扫描,只要下面任何一条被违反,构建就会失败:
- 不存在任何工具,在任何地方,会返回明文密钥的值。
- 不存在任何原始的模型 API 提供方,只有使用你自己登录的内嵌 Claude Code。
- 任何渲染路径上都不存在
eval、Function、vm或child_process。 - 未知工具一律默认拒绝(fail closed),绝不会被悄悄放行。
- 智能体运行时明确禁用了
bash。 - 任何单一授权都无法凑齐「私有数据 + 不可信内容 + 外泄」这个致命三件套。
- 工作流只能是声明式的,不存在任何可执行代码的路径。
如果未来某次提交加入了一个会把密钥交给模型的工具,那这次发布就根本不会出货。
我们拒绝了什么,又转而交付了什么
这道墙逼出了更好的设计,而不是更少的功能:
| 我们拒绝的 | 我们转而交付的 |
|---|---|
| 让 AI 直接调用的原始模型 API | 使用你自己登录的内嵌 Claude Code |
| 让 AI 运行主机命令(bash) | 运行时子进程注入,对密钥无感(secret-blind) |
在工作流里使用 eval() |
声明式 YAML,不存在可执行代码的路径 |
| 图像生成的演示稿(产生云端外发) | 离线文本演示稿,零网络 |
拒绝泄露并没有削减能力,反而让它更聚焦。
关键要点
你完全可以让一个 AI 智能体去做真实的、需要凭据的工作,却不必把你的凭据交给它。密钥在你的机器上保持加密状态,仅在使用的那一刻进入子进程,从不跨入模型的上下文。
这正是 lodos 背后的整个理念:你的 AI 看见你的工作,而不是你的密钥。阅读完整的安全架构,或者下载 lodos,给你的智能体一份工作,而不是一把钥匙。