别让 AI 发现了你的小秘密
承蒙大家厚爱,我的《Go语言之路》的纸质版图书已经上架京东,有需要的朋友请点击 此链接 购买。
Vibe coding / Agentic coding 指的是让 AI 代理直接参与代码库工作:读文件、改代码、跑命令、跑测试,甚至执行一部分自动化流程。这样做在一定程度上降低了开发软件的门槛,提升了研发速度,但是在这个过程中也会容易引入一些安全风险。
特别是对于那些本身不太熟悉软件开发的同学(比如,不知道 node_modules 不需要提交到 Git 的 😂 ),在 vibe coding / agentic coding 过程中一定要注意保护自己在项目中使用到的各种 Secret ,包括但不限于各类云服务密钥、服务密码、APP ID、APP Secret 等。
千万不要为了图省事全都丢给大模型,这是非常危险的操作!
相比于过去古法编程时代只需要注意 “别把密钥提交到 Git”,现在 AI Coding 时代则是要注意 “别让密钥进入 agent 的可见工作区、子进程环境、搜索结果、日志和会话历史”。
时代变了
过去,很多团队开发的时候默认把项目相关 Secret 放进项目根目录的 .env.local,再用 .gitignore 避免把 Secret 提交到代码库。这在“只有人类开发者”的时代已经不算理想,在 agentic coding 场景里就更不够了。
Claude Code 官方文档甚至直接给出了 Read(./.env)、Read(./.env.*)、Read(./secrets/**) 的 deny 示例;这本身就说明,只要敏感文件仍在工作区内,就需要额外的权限控制。
Codex 也会从项目根一路发现 .codex/config.toml 和 AGENTS.md,并按工作目录加载项目级配置与指导文件,这意味着 工作区边界本身 才是第一道安全边界。(Claude)
换句话说,把真实 secret 留在 repo 里,即使没有进 Git,也仍然可能被 agent、子进程、日志系统或搜索功能接触到。
Claude Code 的 permissions.deny 可以阻断这种访问;
Codex 的 sandbox_mode、approval_policy、shell_environment_policy 和 project root discovery 则决定了 agent 在本地到底能看到什么、继承什么、执行什么。(Claude)
四条原则
1. Secret 是运行时配置,不是代码资产
12‑Factor 对这件事讲得非常清楚:配置应放进环境变量;release 阶段是把 build 与当前 config 组合;数据库、第三方 API、SMTP 之类的 backing services 也都应通过 config 中的 locator 或 credentials 接入,而不是写死在源码、镜像或前端包里。(Twelve-Factor)
这条原则在 AI coding 场景里比以前更重要,因为只要 secret 不是“运行时注入”,而是“代码库内容”,你就等于默认允许 agent 在代码理解、全文搜索、命令执行的过程中接触它。(OpenAI Developers)
2. 物理隔离优先于口头约束
最有效的办法不是告诉 agent “不要看 .env.local”,而是不要把真实 .env.local 放在 repo 里。Codex 的默认本地安全模型是“在活动工作区内自动读写、默认无网络”;Claude Code 则把 .claude/settings.json、.claude/settings.local.json、permission rules 和 sandbox 作为能力边界。工程上,这意味着 repo 外的 secret,天然比 repo 内 gitignored 的 secret 更安全。(OpenAI Developers)
3. Secret 不是一个值,而是一个生命周期
OWASP 把 secret lifecycle 拆成 creation、rotation、revocation、expiration,并强调最小权限、自动轮换、可撤销、不可写入日志。对于能够支持 temporal credential 的系统,OWASP 还建议优先考虑动态或短期 secret,以降低复用与横向扩散的风险。(OWASP Cheat Sheet Series)
4. 预防与检测要同时存在
“别泄漏”只是第一层,“泄漏后能立刻阻断和发现”同样重要。GitHub 的 push protection 会在 secret 到达仓库前直接阻止 push;secret scanning 则用于发现已经暴露的凭据。(GitHub Docs)
一套可行方案
推荐在代码仓库内只保留配置契约,而真实的 Secret 保留在代码仓库外的一个单独的目录。这样在项目代码库中使用各种 coding agent 就不会读取到真实的 Secret 了。
myapp/
├─ .codex/
│ └─ config.toml
├─ .claude/
│ ├─ settings.json
│ └─ hooks/
│ └─ protect-secrets.sh
├─ AGENTS.md
├─ .env.example
├─ scripts/
│ └─ run-dev.sh
├─ cmd/
│ └─ api/
└─ internal/
真实 secret 放在 repo 外,例如在本机的 ~.config目录下保存各个项目的 Secret 配置。
~/.config/myapp/dev.env
这符合 12‑Factor 对 config 的要求,也符合 OWASP 对集中化和最小暴露面的原则。对于 agent 而言,这种设计的关键价值是:默认扫描代码库时,根本看不到真实 secret。 (Twelve-Factor)
配置契约
代码库中的.env.example 文件只保留配置契约,知道每个配置是做什么用的即可。
APP_NAME=myapp
APP_ENV=development
APP_PORT=8080
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_DB=myapp
POSTGRES_USER=myapp
POSTGRES_PASSWORD=replace_me
JWT_SECRET=replace_me
WECHAT_APP_ID=replace_me
WECHAT_APP_SECRET=replace_me
运行时注入环境变量
本地运行时,使用类似 run-dev.sh 的脚本,从 repo 外把各种 Secret 注入到运行时环境。
#!/usr/bin/env bash
set -a
source ~/.config/myapp/dev.env
set +a
go run ./cmd/api
这类脚本的作用不只是更优雅地加载环境变量,而是实现把真实 Secret 放在代码库外,只在进程启动前按需注入。这与 12‑Factor 的 config 原则一致。
需要注意的是,source 执行的是 shell 文件本身,因此这个外部 env 文件必须是可信输入。(Twelve-Factor)
Codex 安全配置最佳实践
使用 Codex 时应当把安全边界做在工作区、子进程环境和项目指令里。
Codex 支持用户级 ~/.codex/config.toml 和项目级 .codex/config.toml;项目配置会从 project root 走到当前工作目录逐层加载。默认本地模式下,网络关闭、写权限限制在活动工作区。Codex 还支持 shell_environment_policy 来控制哪些环境变量会传给它启动的子进程,并支持 AGENTS.md 作为项目级指令入口,而且 AGENTS.md 会在任何工作开始前被读取。(OpenAI Developers)
一个适合作为安全基线的 .codex/config.toml 可以写成这样:
model = "gpt-5.4"
approval_policy = "on-request"
sandbox_mode = "workspace-write"
[sandbox_workspace_write]
network_access = false
[shell_environment_policy]
inherit = "none"
include_only = ["PATH", "HOME"]
exclude = ["AWS_*", "AZURE_*", "*_SECRET", "STRIPE_*", "WECHAT_*"]
这套配置的重点有三个:
workspace-write + on-request:默认把 agent 限定在当前工作区内,且高风险动作要显式批准。(OpenAI Developers)network_access = false:避免 agent 在本地默认带网工作。(OpenAI Developers)shell_environment_policy:从一组极小的环境变量开始,避免把云平台凭据或 CI 注入的 token 传给子进程。Codex 文档明确说明这个策略用于控制传给 subprocess 的环境变量,并给出inherit = "none"、include_only = ["PATH", "HOME"]、exclude = ["AWS_*", "AZURE_*"]的模式。(OpenAI Developers)
此外,还可以在 AGENTS.md 中增加相关明确约束来限制 Codex 读取各种环境变量和密钥。因为 Codex 会在开始工作前读取 AGENTS.md。(OpenAI Developers)
# Project security rules
- Never read, print, summarize, or modify `.env`, `.env.*`, `secrets/**`, `*.pem`, `id_rsa`, or credential files.
- Use `.env.example` as the only configuration contract.
- If runtime config is needed, use placeholder variable names only.
- Start the service with `./scripts/run-dev.sh`.
如果你是 monorepo,建议从子目录启动 Codex,例如直接在 services/api/ 下启动,而不是在 monorepo root 打开整个仓库。Codex 默认把含 .git 的目录视为 project root,也支持通过 project_root_markers 改写这一行为;从更小的工作目录启动,能直接减少它的可见上下文和项目级配置发现范围。(OpenAI Developers)
Claude Code 安全配置最佳实践
Claude Code 中可以通过把 deny、sandbox 和 hooks 组合起来限制其读取项目中 Secret 。
Claude Code 的配置体系非常适合做“硬限制”。官方文档区分了 user、project、local 和 managed 四个 scope;
.claude/settings.json适合做团队共享规则..claude/settings.local.json适合做个人覆写,而且它在创建时会被自动加入 gitignore。(Claude)
最重要的一点是:Claude Code 官方明确建议用 permissions.deny 来排除包含 API keys、secrets 和环境文件的路径;匹配到这些规则的文件会从 file discovery 和 search results 中排除,读操作也会被拒绝。(Claude)
一个可直接使用的 .claude/settings.json 例如:
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Read(./config/credentials.json)",
"Bash(cat .env*)",
"Bash(printenv*)",
"Bash(env)"
]
},
"defaultMode": "acceptEdits",
"disableBypassPermissionsMode": "disable",
"sandbox": {
"enabled": true,
"failIfUnavailable": true
}
}
这里的几个关键点都是官方支持的能力:
defaultMode可以设为acceptEdits,避免一上来就进入过度自动化的模式。(Claude)disableBypassPermissionsMode = "disable"可以禁止bypassPermissions,并让--dangerously-skip-permissions失效。(Claude)sandbox.enabled = true且failIfUnavailable = true,可以把 sandbox 变成真正的 hard gate,而不是“沙箱失败后自动退回非沙箱执行”。(Claude)
如果你想进一步阻断危险命令,可以再加一个 PreToolUse hook。
Claude Code 文档说明,PreToolUse 在工具执行前触发,可返回 allow、deny、ask 或 defer,而且决策优先级是 deny > defer > ask > allow。(Claude API Docs)
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-secrets.sh"
}
]
}
]
}
}
protect-secrets.sh 脚本内容如下,拒绝那些能查看 Secret 的操作。
#!/usr/bin/env bash
set -euo pipefail
input="$(cat)"
cmd="$(printf '%s' "$input" | python3 -c 'import sys, json; print((json.load(sys.stdin).get("tool_input") or {}).get("command",""))')"
if echo "$cmd" | grep -Eiq '(^|[[:space:]])(cat|less|more|head|tail|grep|rg|awk|sed)[[:space:]].*(\.env|secrets/|id_rsa|\.pem|credentials\.json)'; then
cat <<'JSON'
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Reading secret-bearing files is blocked by project policy."
}
}
JSON
exit 0
fi
exit 0
这层 hook 的意义在于:即使 prompt 写得很模糊,或者某次工具调用绕开了你原本的预期流程,也会在执行前被再检查一次。(Claude API Docs)
CI/CD 与生产环境最佳实践
除了本地开发阶段,CI/CD 过程和生产环境同样也需要注重 Secret 安全。
OWASP 对 CI/CD 的建议非常直接:把 CI/CD 当成生产系统来加固;最小权限;避免 pipeline output 泄漏 secret;限制谁能看到和修改 pipeline secrets;有条件时,让消费方在运行时自己获取 secret,而不是让 CI/CD 直接把 secret 递给它。OWASP 还明确指出,CI/CD 使用的凭据应尽量短期化、限定授权范围,并且所有操作都要可审计。(OWASP Cheat Sheet Series)
因此,一个成熟的分层策略通常是:
- 本地开发:repo 外
dev.env或操作系统 keychain。 - CI:平台 secrets 或 secret manager,权限只覆盖当前 job 需要的范围。
- 生产:Secret Manager / Vault / 云厂商托管 secret 服务;优先短期 credential 或自动轮换。(OWASP Cheat Sheet Series)
日志、Prompt 和会话历史
很多团队已经不把 secret 提交进 Git 了,但仍会在调试时让 agent 执行 cat .env、printenv、echo $TOKEN,或者把真实密钥直接贴进 prompt。这个风险在 agent 工具里尤其容易被低估。
Codex 文档说明,它默认会在 CODEX_HOME 下保存本地 session transcript;同时,如果启用了 OTel 导出,还会记录 prompts、tool approvals 和 tool results,虽然 log_user_prompt = false 时用户 prompt 默认会被脱敏。(OpenAI Developers)
对于高敏感项目,我的建议是两条:
- 不要让 agent 输出 secret 明文。
- 在敏感仓库里关掉本地 transcript 持久化。
[history]
persistence = "none"
[otel]
exporter = "none"
log_user_prompt = false
这是一个很容易忽视的地方,因为一旦你把 Secret 粘贴进 prompt、stdout 或工具输出,它就可能进入本地历史、外部日志系统或审计流。工程上,最好的办法始终是从源头避免明文 Secret 出现在 agent 交互里。(OpenAI Developers)
最常见的五个反模式
- 把真实
.env.local放在 repo 根目录,只靠“大家别看”来约束。 Claude Code 官方专门提供了Read(./.env)和Read(./.env.*)的 deny 规则,恰恰说明这类文件默认需要被显式保护。(Claude) - 把 secret 当成构建产物的一部分。 12‑Factor 明确要求 build、release、run 分离,release 才把 build 与 config 组合。(Twelve-Factor)
- 开发、测试、生产共用同一套长效密钥。 OWASP 明确强调 secret lifecycle、最小权限和频繁轮换;动态或短期 secret 能显著降低泄漏面。(OWASP Cheat Sheet Series)
- 让 agent 打印环境变量或读取密钥文件进行“调试”。 Codex 默认可保存本地会话历史,且可选导出 prompt 与 tool 事件。(OpenAI Developers)
- 把 push protection 和 secret scanning 当成唯一防线。 它们非常重要,但本质上是预防/检测层,不替代工作区隔离、最小权限和运行时注入。(GitHub Docs)
排查 CheckList
无论你是独立开发了一个 APP、小程序还是上线了一个网站,都可以按下面的流程检查一下是否存在 Secret 安全风险。
- repo 内只允许出现
.env.example、占位配置和非敏感模板;真实 secret 一律放 repo 外。(Twelve-Factor) - Codex 统一使用
workspace-write + on-request + network_access = false,并收紧shell_environment_policy。(OpenAI Developers) - Claude Code 统一启用
permissions.deny、sandbox.enabled、failIfUnavailable,并禁用 bypass mode。(Claude) - 在 Repo 中写明
AGENTS.md/CLAUDE.md级别的项目规则,但不要把它当成唯一防线。(OpenAI Developers) - CI/CD 只持有最小范围、最短时长的凭据;优先让运行时服务自己取 secret。(OWASP Cheat Sheet Series)
- 开启 GitHub push protection 和 secret scanning,作为预防与检测闭环。(GitHub Docs)
- 对暴露事件准备标准化响应:立刻 revoke、重置、清理暴露面、审计访问记录。(OWASP Cheat Sheet Series)
结语
AI coding agent 让开发更快,但也把 Secret 管理的难度提升了。从最开始只要不提交到仓库里就没问题,升级成了“工作区安全设计问题”。需要根据自己的实际场景完成这套组合:真实 Secret 不进 repo、运行时注入、最小权限、自动轮换、工具级 deny/sandbox、以及泄漏检测与快速撤销。







