API Key 会被 AI 看到吗?Gajae-Code 密钥混淆机制(secrets.yml)安全原理完整指南
【免费下载链接】gajae-codeGajae Code MVP项目地址: https://gitcode.com/gh_mirrors/ga/gajae-code
如果你在用 AI 编程助手时配置了各类API Key,一定担心过:这些密钥会不会随着对话内容被发送给大模型服务商?Gajae-Code 内置了密钥混淆(Secrets Obfuscation)机制,通过secrets.yml配置和自动环境检测,在你看不见的层面把敏感值替换成带认证的占位符,从源头保证 API Key 不会进入发给 AI 提供商的消息。
一个真实的担忧:API Key 可能"随对话流出"
AI 编程助手在会话中经常接触你的代码和环境。假设你这样操作:
- 把
OPENAI_API_KEY、ANTHROPIC_AUTH_TOKEN等环境变量留在 shell 里 - 让 AI 读取包含数据库密码、连接串的配置文件
- 让 AI 执行会打印密钥的命令
如果没有任何防护,这些值会原样出现在发给模型的消息文本里。密钥混淆机制就是解决这个问题的:
敏感值在离开进程之前被替换为认证占位符,模型返回的工具调用参数再在显示或执行前还原。
也就是说,对 AI 而言它"看到"的只是#GJC1_…#这样的假名,而不是你的真实密钥。
开启密钥混淆:一步配置
该功能默认关闭,开启方式有两种:
- 在
/settings界面打开 "Hide Secrets" 开关 - 在
config.yml中直接写入:
secrets: enabled: true对应的设置项定义见 settings-schema.ts。
自动防线:环境变量嗅探
开启后,会话启动时会自动收集两类密钥来源,即使你什么都没配置:
| 来源 | 规则 |
|---|---|
| 环境变量 | 变量名匹配KEY/SECRET/TOKEN/PASSWORD/PASS/AUTH/CREDENTIAL/PRIVATE/OAUTH,且值长度 ≥ 8 字符 |
| secrets.yml | 你手动声明的明文或正则条目 |
自动检测的实现就在 index.ts:
const SECRET_ENV_PATTERNS = /(?:KEY|SECRET|TOKEN|PASSWORD|...)(?:_|$)/i;这意味着MY_API_KEY=sk-...这类常见命名会被自动纳入混淆范围,不需要任何手动配置。
secrets.yml:两级配置与合并规则
自定义密钥条目写在 YAML 数组中,两个位置都会被检查:
| 级别 | 路径 | 用途 |
|---|---|---|
| 全局 | ~/.gjc/agent/secrets.yml | 跨项目的明文与正则密钥 |
| 项目 | <cwd>/.gjc/secrets.yml | 仅项目级明文密钥 |
合并规则:项目级 plain 条目会覆盖同值的全局条目,但全局正则条目始终保持生效。项目级文件中的regex条目会被忽略——因为工作区内的文件不受信任提供可执行的正则模式,这是刻意的安全设计(见 index.ts)。
条目字段一览
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type | "plain"或"regex" | 是 | 匹配策略 |
content | 字符串 | 是 | 密钥值或正则表达式 |
mode | "obfuscate"或"replace" | 否 | 默认obfuscate |
replacement | 字符串 | 否 | 自定义替换串(仅 replace 模式) |
flags | 字符串 | 否 | 正则标志(仅 regex 类型) |
实用示例
# 混淆一个特定的 API Key(默认模式,可还原) - type: plain content: sk-proj-abc123def456 # 把数据库密码替换为固定字符串(单向,不可还原) - type: plain content: hunter2 mode: replace replacement: "********"正则条目(仅全局可用):
# 混淆所有 AWS 风格的密钥 - type: regex content: "AKIA[0-9A-Z]{16}" # 单向替换连接串 - type: regex content: "postgres://[^\s]+" mode: replace replacement: "postgres://***"核心原理:#GJC1_…# 占位符是怎么生成的
这是整个机制最精巧的部分,实现在 obfuscator.ts:
1. 进程级密钥进程启动时随机生成一把 32 字节的混淆密钥(index.ts#L9),它只存在于内存中,不写入任何文件。
2. HMAC 认证占位符每个密钥值经HMAC-SHA256(进程密钥, 密钥值)计算,取前 16 字节做 base64url 编码,包装成#GJC1_…#形式的占位符。同一密钥在同进程内始终生成相同占位符,模型因此能"认得"它。
3. 可认证、不可伪造没有那把进程密钥,任何观察者(包括服务商侧日志)无法离线验证某个候选密钥是否就是原文——占位符是一个带密钥的 PRF(伪随机函数),而非简单哈希。
4. 往返还原模型返回的会话上下文和工具参数会被深度遍历,#GJC1_…#占位符还原为原始值后再执行或显示(obfuscator.ts#L274-L286)。所以工具执行时命令里用的仍是真密钥,只是 AI 看不到。
两种混淆模式对比
| 模式 | 行为 | 可还原 | 适用场景 |
|---|---|---|---|
obfuscate(默认) | 替换为认证占位符#GJC1_…# | 是 | 需要工具正常执行密钥的场景 |
replace | 替换为确定性的等长随机串 | 否(单向) | 绝对不希望任何还原的场合,如密码、连接串 |
⚠️ 两个值得注意的细节:
- 进程重启后占位符失效:进程密钥每次启动重新生成,重启前的占位符会保持不透明,这是设计而非缺陷。
- replace 模式泄露长度:确定性替换刻意保持与原密钥等长,密钥长度本身仍可能被观察到。
新手最佳实践清单
- ✅ 开启
secrets.enabled: true,先享受自动环境变量防护 - ✅ 配置文件里写死的密钥 → 加
type: plain条目 - ✅ 一批格式固定的密钥(如 AWS 密钥)→ 全局写一条
type: regex - ✅ 敏感连接串 → 用
mode: replace做单向替换 - ⚠️ 不要把
regex条目写进项目级secrets.yml,它会被静默忽略 - 🔒 需要跨进程稳定的替换值时,显式指定
replacement
小结
Gajae-Code 的密钥混淆机制回答了一个关键问题:API Key 不会以明文出现在发给 AI 提供商的内容中。自动环境嗅探提供零配置兜底,secrets.yml提供精确控制,HMAC 认证占位符保证模型既"认得"密钥又"读不懂"密钥,而往返还原让工具执行完全不受影响。对于长期与 AI 协作的开发者,这是一道成本低、收益高的安全防线。
延伸阅读
- 官方文档:docs/secrets.md
- 加载与合并逻辑:packages/coding-agent/src/secrets/index.ts
- 混淆器实现:packages/coding-agent/src/secrets/obfuscator.ts
- 正则解析:packages/coding-agent/src/secrets/regex.ts
- 远程凭据保险库方案(互补):docs/auth-broker-gateway.md
【免费下载链接】gajae-codeGajae Code MVP项目地址: https://gitcode.com/gh_mirrors/ga/gajae-code
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考