为什么这两个文件要放在第一天
用氛围编程(vibe coding,不自己写代码而是指挥 AI 做软件)第一次做东西时,顺序通常是这样:让 AI 做出界面,接上 Claude 或 ChatGPT 的 API 实现功能,跑得挺好,于是推到 GitHub(存放和共享代码的地方)。事故就发生在最后一步。
AI 写出的代码里,API 密钥常常原样嵌在其中,就是那串以 sk-ant- 或 sk- 开头的长字符串。界面上运行毫无问题,于是就那样留着了。但 GitHub 的公开仓库真的是谁都能看,而且有自动扫描程序全天候地在新推送的代码里寻找形似密钥的字符串。不是人在翻,是机器在扫,所以暴露以分钟计。
密钥落到别人手里后,他们发起的调用会算在密钥主人账上。如果那是云账号的密钥,还能被用来开服务器。所以这两个文件不是「以后有空搞安全时」才补的,而是项目开始那天就要建的。
.env 是什么——把值挪出代码的文件
.env 是 environment(环境)的缩写。它是放在项目文件夹最上层的普通文本文件,里面按「名称=值」的格式一行写一个。装进去的是那些绝不能留在代码里的东西:API 密钥、数据库密码、外部服务的令牌。
代码不再持有值,而是按名称去取。JavaScript 里是 process.env.名称,Python 里是 os.environ 加名称。这样代码里留下的只是名称,不是秘密。下面是实际的差别。
// 坏例子 —— 密钥直接嵌在代码里
const key = "sk-ant-api03-9f2b7c...";
fetch(url, {
headers: { "x-api-key": key },
});
// 这个文件推到 GitHub,密钥也就一起公开了
// 好例子 —— 值从 .env 里取
const key = process.env.ANTHROPIC_API_KEY;
fetch(url, {
headers: { "x-api-key": key },
});
// 代码里只留名称,真正的值不会进入仓库# 名称=值,一行一个。通常不需要引号
ANTHROPIC_API_KEY=sk-ant-api03-9f2b7c...
DATABASE_PASSWORD=b8Kd2mQx.gitignore 是什么——不要上传的清单
git 是记录文件夹里文件如何变化的工具,GitHub 则是把这些记录发布到网上的地方。git 默认把文件夹里的所有文件都当作要记录的对象。所以什么都不做的话,.env 也会一起被上传。
.gitignore 就是那份例外清单。把它放在项目文件夹最上层,一行一个写下不该上传的文件和文件夹。写在里面的东西,git 会当作根本不存在。
# 含有机密的文件 —— 绝不上传
.env
.env.local
.env.*.local
# 随时可以重新下载的东西 —— 没有上传的理由
node_modules/
.next/
dist/
build/
# 操作系统和编辑器留下的残留物
.DS_Store
*.log.env.example —— 只把标签交给别人
一个人用的话,一个 .env 就够了。但只要有人加入协作,或者换一台电脑接着做,问题就来了:.env 不会进仓库,所以对方根本没有这个文件,也不知道需要哪些值。
所以要一并放上 .env.example。它是把名称写好、值留空的样板。它不含机密,因此这个文件是要提交的。拿到的人复制它,改名为 .env,再填上自己的值。
如果在里面写了真实的值,屏蔽 .env 的意义就全没了。样板里永远只留空值和格式。
# 值留空。它只告诉你需要哪些名称
ANTHROPIC_API_KEY=
DATABASE_PASSWORD=已经上传了怎么办——要换,不是删
最常见的误解在这里:发现密钥已经上去了,于是把 .env 加进 .gitignore,然后就安心了。没有用。.gitignore 的意思只是「以后不要开始跟踪尚未被跟踪的文件」,已经提交过的文件会继续被跟踪。而且 git 带着完整历史,就算现在删掉文件,打开旧提交仍然能看到密钥。
所以顺序是固定的。第一,去签发方(Anthropic、OpenAI、AWS 等)的控制台作废那把密钥并重新申请。默认暴露过的密钥已经在别人手里。第二,把文件从跟踪列表里移除。第三,新密钥只写进 .env。
# 1) 先去控制台作废那把密钥并重新申请 —— 永远是第一步
# 2) 从跟踪列表里移除(自己电脑上的文件仍然保留)
git rm --cached .env
# 3) 确认 .gitignore 里有 .env,然后提交
git commit -m "chore: stop tracking .env"
# 旧提交里仍然留着旧密钥 —— 所以第 1 步必须在前氛围编程里绝对不要做的事
新手反复踩的坑并不多。避开下面这六条,大部分事故根本不会发生。
| 不要做 | 为什么危险 | 改成这样 |
|---|---|---|
| 把密钥直接写进代码 | 一进仓库就等于公开 | 放进 .env,用名称去取 |
| 先硬编码,之后再整理 | 中间只要提交一次就永久留下 | 从一开始就用 .env |
| 提交 .env | 最常见的泄露路径 | 先写进 .gitignore |
| 把密钥粘到聊天框、issue、截图里 | 会留在对话记录和图片中 | 只说名称,值自己填进文件 |
| 在 .env.example 里写真实值 | 样板本身成了泄露源 | 只保留空值和格式 |
| 泄露后只删文件 | 历史里的旧密钥仍然有效 | 先重新申请密钥 |
怎么用氛围编程来管理
与其每次都用嘴交代,不如把规则写进文件,这样每次对话都会自动生效。在项目文件夹最上层的 CLAUDE.md(如果混用多种工具则是 AGENTS.md)里放上下面几行即可。
## 机密值的处理
- 绝不要把 API 密钥、密码、令牌直接写进代码。放进 .env,用名称读取。
- 出现新的机密值时,在 .env.example 里加上空名称,并确认 .gitignore 里有 .env。
- 提交前检查跟踪列表里是否混入了 .env 或形似密钥的字符串。
- 不要让我把密钥值粘贴到聊天里。我会自己写进文件。怎么确认真的挡住了
写下规则不等于生效。三条命令就能确认。你也可以直接对 AI 说「检查一下现在有没有不该上传的文件正在被跟踪」,它会跑同样的检查。
# 这次提交会上传的清单 —— 这里不该出现 .env
git status --short
# 确认 .env 确实被忽略(打印出规则行就是正常)
git check-ignore -v .env
# 已被跟踪的文件里有没有名字带 env 的
git ls-files | grep env上线之后——值住在两个地方
要让别人也能用你做的东西,就得经过部署服务,而这里还有一道坎:既然 .env 不进仓库,部署后的服务器上就没有这个文件。
解决办法很简单。Vercel、Railway 这类服务的设置界面里都有环境变量(Environment Variables)一栏。把 .env 里同样的名称和值填进去,部署后的服务器就会从那里读取。本地是 .env,服务器是控制台——记住值住在两个地方就行。不要把 .env 文件本身传到服务器上。
现在就开始
五步就够。(1) 在项目文件夹最上层建 .gitignore,写上 .env 一行。(2) 建 .env 文件,把密钥挪进去。(3) 删掉代码里嵌着的密钥,改成按名称读取——直接让 AI「把代码里的密钥挪到 .env」就行。(4) 在 .env.example 里只留名称。(5) 用 git status 确认 .env 不在清单里,然后提交。
两个文件,十分钟的事。而这十分钟能把泄露事故和意外账单整个抹掉。
