全部指南
严重程度:严重暴露的密钥

在客户端 JavaScript 中暴露的 API key

任何发送到浏览器的内容——内联脚本、打包 JS、source map——都能被任何人读取。嵌入前端代码的 secret key(OpenAI、Stripe secret、AWS、数据库 URL)实际上等于已经公开发布。

这是什么

构建工具会在编译时把环境变量内联进来。如果你在客户端代码中引用了某个密钥,它就会出现在每个访客都会下载的 JavaScript 打包文件里。

这里有一个关键区分:publishable/anon key(Stripe 的 pk_live、Firebase 的 web 配置)本就是设计为公开的,但 secret key(sk_live、service role key、各服务商的 API key)不是,绝不能进入浏览器。

为什么重要

泄露的服务商密钥会让任何人在你的账户上疯狂消耗——LLM 账单、短信费用、云支出——或访问该密钥所授权的数据。

攻击者会大规模抓取 JavaScript 打包文件,寻找密钥特征。一个提交上线的密钥通常在几分钟内就会被滥用。

如何修复

把密钥留在服务端

从服务端路由或后端调用第三方 API,绝不从浏览器调用。浏览器与你的 endpoint 通信;密钥保存在你的 endpoint 上。

// app/api/summarize/route.ts (server-only)
export async function POST(req: Request) {
  const { text } = await req.json();
  const r = await fetch("https://api.openai.com/v1/responses", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` },
    body: JSON.stringify({ model: "gpt-4o-mini", input: text }),
  });
  return Response.json(await r.json());
}
谨慎使用 NEXT_PUBLIC_ 前缀

在 Next.js 中,只有带 NEXT_PUBLIC_ 前缀的变量才会暴露给浏览器。绝不要把密钥放在这个前缀后面——它只用于可以安全公开的值。

轮换已泄露的密钥

如果某个密钥已经发送到浏览器,就吊销并重新签发它。仅从下一个构建中移除它,并不能让已被抓取的副本失效。

常见问题

我的 Stripe publishable key(pk_live)放在前端有问题吗?

没有——publishable key 本就是公开的。风险在于 secret key(sk_live),它必须留在服务端。

我能不能直接把密钥混淆一下?

不行。混淆很容易被还原。唯一的解决办法是让密钥完全不进入客户端。

你的应用受影响了吗?

AppSafe 一次免费扫描,就能检查这个问题以及数十项其他问题。

免费扫描我的应用