AI Agent时代API Key安全指南:环境变量防不住Agent读密钥,最小权限临时密钥才是正解

前言:AI Agent能通过echo一句命令读取终端环境变量,API Key安全在Agent时代面临全新威胁模型。本文剖析npm供应链攻击如何窃取密钥,解释环境变量真正防的是什么,并给出临时密钥、最小权限、密钥管理服务等实操方案,让被偷走也无所谓。

AI让我把Key设到终端里,说这样更安全。它没骗我,但我差点就信了

前两天用Devin干活,要接一个API,得给它一个TYPESAFE_API_KEY。

我在对话框里刚敲了半个字,它先把话拦下来了,给了我三个选项。

第一个,你自己在终端里设置,set TYPESAFE_API_KEY=ts_xxx,它特意标了个(推荐,key 不经过对话)。

第二个,把key直接发它,它跑完即弃,不写进任何文件。

第三个,发到对话里,它用环境变量的方式注入运行,同样不落盘。

我当时盯着那个「推荐」看了两秒,第一反应是,哦,那肯定选第一个啊,key都不经过对话了,多安全。

然后我愣了一下。

等等,不对劲。。。

这哥们儿,是能在我终端里执行任意命令的。我把key设置在它跑命令的同一个终端里,跟把钥匙塞进它口袋里,有什么区别?

你大可以现在就打开终端试一下。export TYPESAFE_API=tS12345,然后敲一句echo $TYPESAFE_API。

完整的值,明明白白打印出来。

而Agent拥有的,就是这同一个终端的最高权限。它想读,一句echo就够。它甚至懒得echo,随便写两行代码,Node里遍历一遍process.env,Python里翻一下os.environ,你机器上所有带KEY、TOKEN、SECRET字样的变量,一锅端。

所以第一种方式,防不住Agent读key,一丁点都防不住。

这事我一开始想得特别简单,觉得环境变量这玩意儿就是个安慰剂,看起来安全,实际裸奔。这个结论我在脑子里放了大概三秒钟,然后觉得,不对,好像也不是这么回事。

因为环境变量这个东西,软件行业用了几十年了,还真不是拍脑袋定的。2011年有个叫Twelve-Factor App的方法论,把配置和代码分离列成云时代应用的基本原则,密钥走环境变量就是那条标准答案。要是纯安慰剂,不可能全世界用到现在。

那它到底防的是什么?

我顺着这个疑问往下捋,发现环境变量的安全,从来就不是防「运行时有人偷看」的。它防的是另一件事,代码泄露。

你想想看,如果没有环境变量,密钥写哪?写在代码里。写在代码里,就会跟着git push进仓库。仓库一旦公开,或者哪天一个内部仓库权限配错了,全网白送。这是现实中密钥泄露的最大来源,没有之一。环境变量最大的功劳,就是把密钥从代码里拎出来,逼着你把「代码」和「机密」拆开。

CI里还有一层兜底,GitHub Actions这种系统,你注册过的secret,不管哪个环节把它打印到日志里,都会被自动替换成***。日志系统帮你拦着。

所以环境变量不是没用,它的安全有前提,你机器上跑的东西是可信的。

问题就出在这。Agent时代,这个前提松动了。

我不是吓唬你,运行时偷环境变量这事,早就不是假想敌了。今年6月,AI Agent开发框架Mastra的npm生态出了一次供应链攻击,88分钟之内,144个包被重新发布,攻击者往里面塞了一个仿冒dayjs的恶意依赖,装包的时候自动执行脚本,扫你的进程环境变量,专挑OPENAI_API_KEY、ANTHROPIC_API_KEY、AWS_ACCESS_KEY_ID、GITHUB_TOKEN这些下手,加密之后传到攻击者自己的服务器。

你npm install的时候,它在遍历你的环境变量
你npm install的时候,它在遍历你的环境变量

更早还有个叫Shai-Hulud的蠕虫,波及400多个npm包,干的也是同一件事,收集本地、CI、云环境里的机密信息,然后外传。

你看,这些恶意代码干的事,跟一句echo $TYPESAFE_API没有任何区别。就是遍历环境变量。

那回到Devin给的三个选项。第一种明明防不住Agent读取,它凭什么标「推荐」,凭什么说更安全?

我琢磨了一下,发现这里其实有两个「安全」,长得一模一样,但防的是两种贼。

第一种方式,你在终端里手动set,key只存在于沙箱实例的内存里。你的「对话上下文」里,从头到尾没有出现过这串明文。这段对话被存到云端、被拿去训练模型、被人工审核看到、或者你把对话链接分享给同事,key都不会跟着泄露。沙箱一销毁,key彻底蒸发。

第二种方式,key发在聊天框里。这串明文不光进了终端,还永久躺在了你和AI的对话数据库里。攻击面完全不是一个量级。

所以Devin没骗你。它说的安全,是聊天记录的安全,不是运行环境的安全。key不经过对话,但经过它的手,它必须读到,不然它怎么替你调API。

就像酒店房间里的保险箱。你知道酒店员工理论上打得开,但你还是会把护照锁进去。因为它防的是保洁顺手一翻,不是防酒店本身。你不能因为它防不了酒店,就说它没用。

写到这儿,我得先站在另一边说两句。我特别理解很多人看到这里的反应,那不等于裸奔吗,那还聊什么。

你不是安全工程师,你不想研究什么威胁模型,你只是想把活干完。把key发到对话框里是最顺手的路径,Agent还贴心地告诉你不落盘、跑完即弃。这种心情我太理解了,我自己图省事的时候也这么想过。

但恰恰因为你不是安全工程师,「安全」这个词才最容易从你耳边滑过去。Agent说的每一句都没错,只是它没把「安全」的定语说全。

那实战里到底怎么办?我自己也还在摸索,说几个我觉得靠谱的,按门槛从低到高排。

1、永远只给临时的、最小权限的、随时可撤销的key。测试环境的key,额度限死,用完就吊销。别把生产环境的主API Key交给任何Agent,一个字都别给。这一条零门槛,今天就能做,而且做到之后,前面聊的一半风险直接消失。

2、安全要求高的本地工具,别走环境变量。让密钥通过管道传给进程的stdin,或者用不会回显的交互式输入,密钥只活在内存里,子进程继承不走这条路。

3、再往上一层,上密钥管理服务,HashiCorp Vault这类。进程启动时用IAM身份去动态换真key,环境变量里只放身份标识,不放真key。

4、如果实在信不过正在跑的Agent,最釜底抽薪的是网络隔离。把它扔进严格限制出站流量的容器里,它就算把key读得滚瓜烂熟,也发不出去。

坦率的讲,2和3对普通人有点重,配置成本摆在那,我自己都没完全跑通。但第1条没有任何借口。

聊到这儿,我想起一句老话,锁是防君子的,不防小人的。

其实安全这件事,从古到今就没有「绝对安全」这个选项,只有「防谁」。环境变量这把锁,防的是代码仓库里的明文、CI日志里的泄露,防不了你亲手请进家门、还递给它终端权限的那位客人。威胁模型一变,同一把锁的意义就全变了。

锁防的是顺手的人,不是有钥匙的人
锁防的是顺手的人,不是有钥匙的人

而Agent时代最大的变化,就是你家里第一次常住进来一个有root权限的客人。它大概率是好人。但Mastra事件里那144个包,在被投毒之前,也个个都是好人。

所以再回到Devin那句(推荐,key 不经过对话)。

现在我会在后面替它补上半句它没说出口的话,key不经过对话,但经过它的手。

这不是它坏,是它必须这样。它读不到key,就没办法替你干活,这是这份工作的物理定律。

你真正能做的,从来不是让它读不到,而是让它读到的那个东西,被偷走也无所谓。

临时。最小权限。随时可撤销。

就这三个词,比任何环境变量都安全。

你在凝视深渊,深渊也在凝视你…

/ 注意,本项目仅在 SyncMein 的500人小群分享。
/ 谢谢你看完了我的文章,我们下次再见吧。
/ 作者:明察
/ 投稿或爆料,请联系邮箱:mingcha@gqmg.com

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容