AI 读了 .env,密钥会原样发给大模型吗?WES Code 的密钥打码(附实测)

目录


让 AI 帮忙查「为什么连不上数据库」「为什么调第三方接口返回 401」,它几乎一定会去读 .env、config.yaml,或者跑一下 env 看环境变量。

问题是,它读到的内容会作为工具结果,原样发给大模型。密钥一旦离开本机,就不归你管了。

WES Code 在这一步加了一道处理:工具结果交给模型之前,先按一张规则表给密钥打码,只遮值、保留键名,不拦输出。这篇讲它在哪几个地方打码、认得哪些密钥、我用 12 条样例实测的结果,以及它遮不住的情况。


一、在哪几个地方打码

位置 打码的是什么
工具结果 读文件、跑命令等工具的输出
模型的回复 推送到你面前之前
需要你确认的提示 弹窗里展示的命令和内容

工具结果这一处最关键。一个工具结果出来以后会分成三份:界面上显示一份,发回模型一份,太长的还会写到磁盘上。打码放在分叉之前做,三份拿到的都是遮过的。所以 AI 读了 .env,模型看到的就是 [REDACTED]。

比如 AI 读到这样一份 config.yaml(内容是我编的):

yaml 复制代码
server:
  port: 8080
database:
  dsn: postgres://shop:Sh0pPassw0rd2024@localhost:5432/shop
llm:
  provider: openai
  api_key: sk-proj-7c1e9a4b2d8f6e3a
github:
  token: ghp_9a8b7c6d5e4f3a2b1c0d

交给模型的是这样的(用第三节那套代码实际跑出来的):

yaml 复制代码
server:
  port: 8080
database:
  dsn: postgres://shop:[REDACTED]@localhost:5432/shop
llm:
  provider: openai
  api_key: [REDACTED]
github:
  token: [REDACTED]

端口、服务商、连接串里的用户名和主机都还在,AI 照样能看出配置哪里不对,只是拿不到那三个值。

这套密钥规则是内置的,不能通过配置关掉。


二、它认得哪些密钥

规则一共 32 条,分两类。

看格式的 11 条:

类型 长什么样 怎么遮
键值对 OPENAI_API_KEY=...、api_key: ...、"token": "..." 只遮值,键名保留
数据库连接串 postgres://用户名:密码@主机 只遮密码,用户名和主机保留
请求头 Authorization: Bearer ... 只遮令牌
私钥 -----BEGIN ... PRIVATE KEY----- 整段 换成 [REDACTED PRIVATE KEY]
JWT eyJ 开头的三段式令牌 换成 [REDACTED JWT]
云服务密钥 AWS 的 AKIA...、Azure 订阅密钥等 只遮值

键值对认的是键名里的关键词:api_key、apikey、token、secret、password、passwd、credential、access_key。前后带别的字符也算,所以 MY_OPENAI_API_KEY、providerApiKey 都认得。等号、冒号都认,.env、YAML、JSON 三种写法都覆盖。

看前缀的 21 条: 很多服务商的密钥有固定开头,比如 OpenAI 的 sk-、GitHub 的 ghp_、Hugging Face 的 hf_、npm 的 npm_、阿里云的 LTAI。这类不需要键名,光看开头就能认出来。


三、实测:12 条样例

我把这套规则的代码原样拿出来,写了个小程序,对 12 条样例各跑一遍。样例里的密钥都是我编的。

go 复制代码
package main

import (
	"fmt"

	"credscandemo/credscan" // WES Code 的密钥规则,原样复制
)

func main() {
	samples := []string{
		`OPENAI_API_KEY=sk-proj-4f8a1c9e2b7d6a3f`,
		`api_key: sk-9d2c7b4e1a8f3c6d`,
		`{"token": "a1b2c3d4e5f6g7h8"}`,
		`DATABASE_URL=postgres://app:S3cretPassw0rd@localhost:5432/shop`,
		`Authorization: Bearer abcdef1234567890`,
		`eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NSJ9.c2lnbmF0dXJlMTIz`,
		"-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA7x9k\n-----END RSA PRIVATE KEY-----",
		`OPENAI_API_KEY=your-api-key-here`,
		`aws_access_key_id = AKIAIOSFODNN7EXAMPLE`,
		`ghp_abc123def456ghi789`,
		`换成 sk-short 试试`,
		`password: abc123`,
	}
	for _, s := range samples {
		fmt.Println(credscan.Redact(s))
	}
}

结果:

# 输入 输出
1 OPENAI_API_KEY=sk-proj-4f8a1c9e2b7d6a3f OPENAI_API_KEY=[REDACTED]
2 api_key: sk-9d2c7b4e1a8f3c6d api_key: [REDACTED]
3 {"token": "a1b2c3d4e5f6g7h8"} {"token": "[REDACTED]"}
4 DATABASE_URL=postgres://app:S3cretPassw0rd@localhost:5432/shop DATABASE_URL=postgres://app:[REDACTED]@localhost:5432/shop
5 Authorization: Bearer abcdef1234567890 Authorization: Bearer [REDACTED]
6 一段 JWT [REDACTED JWT]
7 一段 RSA 私钥 [REDACTED PRIVATE KEY]
8 OPENAI_API_KEY=your-api-key-here 原样不动
9 aws_access_key_id = AKIAIOSFODNN7EXAMPLE aws_access_key_id = AKIA[REDACTED]
10 ghp_abc123def456ghi789 ghp_[REDACTED]
11 换成 sk-short 试试 原样不动
12 password: abc123 原样不动

有几条值得单独说:

  • 第 1 条 :值本身也是 sk- 开头,但键值对规则先执行,先把值遮掉了,所以输出里 OPENAI_API_KEY= 还在,读起来仍然是一行配置。
  • 第 4 条 :只遮了密码,用户名 app、主机和库名都在。查「连不上数据库」时,这些信息照样有用。
  • 第 8 条 :文档里常见的占位写法,不打码。键值对规则会跳过含 your、example、placeholder、<、${ 这类词的值,不然每个 .env.example 都会被遮得面目全非。
  • 第 9 条 :这是 AWS 官方文档里的示例密钥,结尾是 EXAMPLE。键值对规则把它当成占位符跳过了,但「AKIA 开头」这条规则不看占位词,照样遮。按前缀认的规则都是这样:格式对上了就遮,不因为里面有个单词就放过。

另外,同一段文字打两次码,结果不变:已经是 [REDACTED] 的地方不会被再遮一遍。

第 11、12 条为什么没遮,放到第五节说。


四、不拦输出,日志里也不留值

打码只替换值,不会因为发现了密钥就把整个结果拦下来。拦下来的话,AI 读 .env 查问题这件事就做不成了;遮掉值,结果里其余的配置照样能用。

发生打码时,日志里记一条警告,内容是命中了几条、第一条是哪条规则,不记值本身。命中的描述长这样(第 1 条样例的扫描结果):

secret_assignment env_secret REDACTED 24 chars

openai_key vendor_key REDACTED 21 chars

只留规则名和长度。长度能帮你区分这是一个 8 位的密码还是一串 64 位的令牌,又不会泄露内容。


五、它遮不住的情况

  • 太短的值 :键值对的值少于 8 个字符不遮,比如第 12 条的 password: abc123;按前缀认的,前缀后面少于 10 个字符不遮,比如第 11 条的 sk-short。这是为了不把正文里随手提到的 sk-short 当成密钥
  • 键名不在关键词里 :DB_PASS=Hunter2Hunter2 我也试了,没遮。pass 不在关键词里,只有 passwd、password 算
  • 值里碰巧带了占位词 :api_key: my_real_key_9f8e7d 没遮,因为值里有 my_。同样的值去掉 my_ 就遮了
  • 自定义格式:公司内部自己定的令牌,键名不带关键词、开头也不是已知前缀,认不出来
  • 报错结果:工具返回的是错误结果时,不走工具结果这一步打码
  • 只管发出去的内容 :打码改的是发给模型、显示在界面上的文字,磁盘上的 .env 不会被改

「键名不在关键词里」和「值里带占位词」这两条,是用下面这个测试核对的:

go 复制代码
func TestNotCovered(t *testing.T) {
	for _, s := range []string{
		`DB_PASS=Hunter2Hunter2`,
		`api_key: my_real_key_9f8e7d`,
		`api_key: real_key_9f8e7d`,
	} {
		t.Logf("%s  =>  %s", s, credscan.Redact(s))
	}
}

前两条原样输出;第三条是同一个值去掉 my_,输出变成了 api_key: [REDACTED]。


小结

  1. 工具结果在交给模型之前先打码,模型的回复、需要你确认的提示也会打码;内置规则关不掉
  2. 32 条规则:11 条看格式,21 条看服务商前缀;只遮值,键名、用户名、主机都保留
  3. 文档占位符不遮,AWS 文档示例这种真格式照样遮;不拦输出,日志里只留规则名和长度
  4. 太短的值、不认识的键名和格式、值里带占位词的、报错结果,遮不住

文中的密钥打码是 WES Code 里的功能,官网是 weisyn.com。你们项目里的密钥一般放在哪,让 AI 读配置时担心过吗,欢迎评论区聊聊。

觉得有用的朋友,欢迎点赞、收藏、关注,后面会继续分享 AI 编程的实战经验。

相关推荐
吴佳浩1 小时前
单卡5090跑125B 大模型:从安装、验证到基准测试的完整操作教程
人工智能
泡海椒1 小时前
JQuick-Excel 实战:用 STYLE 配置单元格与区域样式
开发语言·python·excel
数智工坊1 小时前
视觉SLAM第13讲|工程落地:双目视觉里程计系统架构设计与性能优化
人工智能·深度学习·线性代数·性能优化·矩阵·系统架构·机器人
newsxun1 小时前
校园餐全链条管理有了“实操手册” ——《学校食品安全与营养健康管理操作指南》在成都发布
大数据·人工智能
空堂与归1 小时前
GPT-6 智能界面上线,对话式 Agent 真没戏了?
人工智能·gpt·ai
码流子1 小时前
05-AI数字人
人工智能
威联通网络存储1 小时前
TS-h1283XU-RP 在医药批记录与检验场景的部署
网络·python
moonsims1 小时前
AiBrainBox “高校算法创新”与“真实无人系统”的国产化智能端侧科研平台-高校 × 企业:构建面向低空与具身智能的产学研协同创新平台
人工智能
Patrick在香港1 小时前
时间戳凭空早了 8 小时:datetime.utcnow() 弃用实测与漂移复盘
数据库·python·标准库·datetime·时区·弃用