「都说安全很重要,可到底该先从哪儿下手?」——这就是写给这样的个人开发者的、最最开始的第一步。不是为了吓唬你,而是只把从今天就能动手的事,按顺序讲清楚。
为什么要从秘密的值(.env 和 API key)开始
真实发生的事故,很多都不是什么高级攻击,而是从**「秘密的值泄露了」**开始的。本站拿来作为出发点的两个案例,也都是秘密泄露:
也就是说,只要能守住秘密的值,常见事故里的绝大多数就都能防住。所以我们就从这里开始。
.env 是什么(30 秒讲完)
.env(点 env)是把应用要用的秘密的值汇总到一起的配置文件。里面装着数据库的密码、外部服务的 API key、给会话加密用的密钥等等。正因为这些秘密的值都集中在一个文件里,一旦泄露就会全部暴露,所以要最优先地守好它(→ 术语:.env 是什么)。
秘密的值可以放在哪里(.env、环境变量、密钥管理服务)
存放秘密的值,不只有 .env 一种办法。按规模和用途来选。
| 存放位置 | 适合 | 要注意的地方 |
|---|---|---|
.env 文件 | 本地开发、单台小型服务器 | 不放进 Git,也不放进公开目录 |
| 托管服务的环境变量设置(在控制台中输入) | 生产环境 | 限制谁能访问控制台;别让值出现在构建日志里 |
| CI(自动测试和部署)的 secrets | GitHub Actions 等自动化流程 | 在 CI 中运行的外部工具能看到这些值;只传入权限够用的 key |
| 密钥管理服务 | 多人或多台服务器 | 读取权限保持最小,并定期轮换 |
关于 CI 的 secrets,2021 年的 Codecov 事件很有参考价值。在 CI 中运行的一个外部工具被篡改,保存在 CI 环境变量里的秘密的值被拿走了(→ Codecov 事件)。
可以公开的 key 和绝不能公开的 key
有些 API key 本来就是要放进浏览器或手机 App 里发布的,有些则只能在服务器上用。凡是发送到浏览器的东西,都要假定任何人都能读到。
| 例子 | 能否公开 | 理由 |
|---|---|---|
Stripe 可发布密钥(以 pk_ 开头) | 可以 | 文档说它可以放进前端代码;它不能执行创建扣款这类敏感操作 |
Stripe 私密密钥(sk_)或受限密钥(rk_) | 不可以 | 它们能执行服务端操作;文档建议使用缩小了权限的受限密钥 |
带 NEXT_PUBLIC_ 的 Next.js 环境变量 | 会被公开 | 它的值会在构建时内联到浏览器端 JavaScript 中 |
不带 NEXT_PUBLIC_ 的 Next.js 环境变量 | 不公开(仅服务端) | 秘密的值放在这里 |
拿不准时,去查该服务的官方文档,看这个 key 能否在客户端使用。如果文档没写,就把它当作不能公开的 key。
API key 泄露后会发生什么
API key 是「让服务把调用者当作你本人来对待的一串字符」。它一旦落到别人手里,损害就会这样扩大。
被偷走的 key 往往会有时间差地被转卖、滥用,「泄露刚发生时啥也没有→过些天账单突然飙升」是很典型的套路。所以「等发现了再说」就晚了,关键是从一开始就别让它泄露。
怀疑 key 已经泄露时该怎么做
只从代码里删掉 key,是挡不住的。GitGuardian 的 2025 年报告发现,2022 年在公开仓库中泄露的秘密信息,有 70% 到报告发布时仍然有效。请按以下顺序处理。
- 在签发方的控制台作废或轮换这个 key。如果服务提供宽限期,就让泄露的 key「立即」失效
- 在生产环境设置新 key,确认应用能正常运行
- 查看签发方的使用记录和账单,有没有不是你的活动
- 从文件和 Git 历史中删除,可以放到作废之后再做(单靠删除,挡不住已经拿到副本的人)
详情见 GitHub 上公开的 54 万个凭据仍然有效。GitHub 默认开启「推送保护」,可以阻止个人账户把秘密信息推送到公开仓库,但并不能拦住所有种类的秘密信息。
从今天就能做的 4 个防护办法
不需要什么难的东西。先做好这 4 件就行。
不公开
别把 .env 和应用本体放到 Web 上能看见的目录(公开根目录)里。对外公开的只能是 public/。在虚拟主机上的具体操作步骤见 这篇文章。
不提交
在 .gitignore 里加上 .env*(!.env.example 可以放行)。只共享不带值的样例。一旦提交过就能从历史里恢复出来,所以从一开始就别放进去。
泄露就全部更换
不是只换一个,而是把有可能泄露的钥匙全部轮换。优先顺序是「外部 API · OAuth → 加密密钥 → 邮件 → DB」。按「已经被看到了」的前提来处理。
自查
偶尔确认一下自己的网站,看 /.env 能不能从外面打开(见下)。
自查(只对自己拥有的域名做)
# 返回 200 且带正文就是被公开了。403/404 则暂时没问题
curl -sI https://你的域名/.env | head -1
curl -sI https://你的域名/.git/config | head -1养成每次上线都确认一遍的习惯,就能尽早发现放置位置的失误。
除了从外部能看到的,也要检查 Git 和构建产物(在你自己的仓库中运行)。
# .env 是否曾被提交过?(有输出就说明某个时候提交过)
git log --all --oneline -- .env
# Git 当前跟踪的类似 env 的文件(.env.example 以外的都要看一下)
git ls-files | grep -i "env"要确认没有 key 被发送到浏览器,可以在构建产物文件夹(Next.js 是 .next/static)中搜索 key 的前几个字符。也可以在发布前把代码粘贴到密钥泄露扫描里检查(不会发送到任何地方)。
容易犯的错 vs 正确的第一步
容易犯
- 反正先跑起来了,就把
.env直接放在 public 下 - 想着「以后再删」,把
.env提交进 git 一次 - 只换那一个看到被滥用的钥匙
正确的第一步
- 本体放在公开根目录之外,只公开
public/ - 从一开始就用
.gitignore把.env排除掉 - 泄露了就把 env 里的钥匙全部轮换
下一步
走到这一步,接下来就是「自己技术栈里危险的默认设置」和「不在已公开漏洞(CVE)上落后的机制」了。别着急,一个一个来。
出处
- Next.js「How to use environment variables in Next.js」(16.x)— nextjs.org
- Stripe Docs「API keys」— docs.stripe.com
- Laravel 13.x「Configuration」(Environment File Security)— laravel.com
- GitHub Docs「About push protection」— docs.github.com
- GitGuardian「The State of Secrets Sprawl 2025」— blog.gitguardian.com
接着读
- 泄露之后:GitHub 上公开的 543,699 个凭据仍然有效:为什么必须作废,而不只是删除
- Gyazo 数据泄露(2026 年 9 月):泄露了什么,用户今天该做什么(英文)
- 术语:.env 是什么 / CVE 是什么 / RCE 是什么
- 防护:在虚拟主机上别让 .env 被公开 / Next.js 的 CVE 跟进
- 事故:API key 被偷走后遭盗刷的事
- 事故:用偷来的 key 导致账号被封的事
- 供应链:一次 npm install 就偷走你的凭据时(别人的代码在存放你的 key 的环境中运行)