跳转到内容

密钥:那把不能进前端的钥匙

你写在前端代码里的任何东西,全世界都能看到。包括你以为藏起来的那个 API Key。

密钥(key / token / secret / 凭证)就是你向某个服务证明「我是我」的那串字符

常见的有:

  • OpenAI / Claude 的 API Key(拿它就能用你的钱调 AI)
  • 数据库连接串(拿它就能读写你的数据)
  • 云服务的 access key(拿它能干的事更多)
  • 短信、支付、邮件服务的密钥(拿它能以你的名义发东西、收钱)

它们的共同点:谁拿到,谁就是你。

你跟 AI 说:「帮我接一下 OpenAI,让用户能在页面上问问题。」

AI 会给你一段能跑的代码。**它确实能跑。**在你电脑上完美工作。

但它八成会把 Key 直接写进前端——因为那是最短、最直接、最容易跑通的写法。AI 优化的是「能跑」,不是「能上线」。

代码在哪运行 谁能看到代码
前端 用户的浏览器里 所有人
后端 你的服务器上 只有你

这是全部的关键。前端代码必须先下载到用户电脑上才能运行——这是浏览器的工作原理,无法改变。

所以任何「藏在前端」的想法都是幻觉:

  • ❌ 写在变量里 → 能看到
  • ❌ 混淆压缩过 → 能看到(搜一下就还原了)
  • ❌ 拆成几段拼起来 → 能看到
  • ❌ 存在 .env 文件里但被打包进前端 → 能看到(这个最坑,下面单讲)

很多人以为把 Key 放进 .env 文件就安全了,因为「.env 不进 git」。

但打包工具会把带特定前缀的变量塞进前端产物里。

框架 这个前缀 = 会进前端 = 全世界可见
Vite / Astro PUBLIC_VITE_
Next.js NEXT_PUBLIC_
Create React App REACT_APP_
Nuxt NUXT_PUBLIC_

这些前缀的存在,就是为了「我知道这个会公开,我故意的」。

所以看到 NEXT_PUBLIC_OPENAI_API_KEY 这种东西——那个 Key 已经公开了.env 一点忙都没帮上。

让密钥只待在后端,前端向你自己的后端要东西。

❌ 危险
浏览器 ──(带着你的 Key)──> OpenAI
↑ Key 在这里,所有人可见
✅ 安全
浏览器 ──(不带 Key)──> 你的后端 ──(带 Key)──> OpenAI
↑ Key 只在这里,用户看不到

跟 AI 提需求时,把这句话加上:

密钥必须只在服务端使用,前端不允许出现任何 API Key。请用服务端路由/API route 做一层代理,前端只调我自己的接口。

不要相信任何人说「应该没问题」,包括 AI,包括我。自己查。

构建之后,在产物目录里搜你的 Key:

终端窗口
# 把 dist 换成你的构建输出目录(可能是 build / .next / out)
grep -r "sk-" dist/ 2>/dev/null
grep -riE "api[_-]?key|secret|token|password" dist/ 2>/dev/null | head -20

**搜到了 = 已经泄露。**没搜到再往下。

  1. 打开你部署好的站
  2. F12(Mac 上 Cmd+Option+I),切到 Network 标签
  3. 刷新页面,正常用一遍所有功能
  4. 挨个点开请求,看 HeadersResponse

任何一处出现你的 Key = 泄露。

Key 有没有在某次提交里进过仓库?

终端窗口
git log -p --all -S "sk-" 2>/dev/null | head -40
  • 有人把云服务密钥推进了 GitHub 公开仓库,被人拿去跑算力,账单 55,444 美元
  • 有人在前端硬编码了数据库凭证,被人在 Network 面板直接捡走
  • 扫描显示,20,052 个 AI 快速搭建的上线站点中,11.04% 正在泄露数据库凭证

这些不是「小概率事件」。公网上有机器人 7×24 小时专门扫这个,你的站上线几分钟内就会被扫到。

  • 说得清「前端」和「后端」的区别,以及为什么前端藏不住东西
  • 认得出 NEXT_PUBLIC_ / VITE_ / PUBLIC_ 这类前缀意味着什么
  • grep 在构建产物里自己搜一遍密钥
  • 用浏览器 Network 面板自己查一遍
  • 万一泄露了,知道第一步是去作废密钥,不是删代码

终极验收:把你的仓库地址交给一个陌生人,他花 10 分钟用 DevTools 翻遍你的站,找不到任何一个 Key