Vercel 预览部署泄露了我全部密钥,怎么发现的

作者: Trove Deck Solution 发布: 2026-06-11 阅读时长: 8 分钟

一条 Slack 消息在周四晚上 11:47 落进我的通知栏。客户写道:”你的 Stripe 密钥好像暴露了,你看到了吗?” 我盯着屏幕愣了三秒,打开预览链接,Application 面板里赫然躺着 sk_live_51...——完整、可读、可扣款。

Vercel 预览部署把我的生产支付密钥递给了任何拿到 URL 的人。部署默认公开,我没有做任何限制配置。

为什么 Vercel 预览部署默认暴露密钥

Vercel 会在每个非 production 分支推送时自动创建预览 URL。这些 URL 对所有拥有链接的人可见。如果你的构建流程、客户端代码或 source map 引用了环境变量,这些变量就会出现在预览中。Vercel 文档明确说明:除非你为特定分支配置了独立的变量值,否则预览环境会继承 production 的变量绑定。

这意味着一个配置不当的 NEXT_PUBLIC_STRIPE_KEY,或者更糟的 NEXT_PUBLIC_API_SECRET,会被打包进 JavaScript bundle,任何访问者都能看到。

定义: Vercel 预览部署 = 基于 git 分支生成的临时公开应用副本,在未配置分支限制时会继承 production 的环境变量访问权限。

我是怎么发现泄漏的

发现纯属偶然。我让客户通过预览链接评审新功能,对方恰好会用 Chrome DevTools。大多数审查者根本不会打开开发者工具——这恰恰是最危险的地方。

以下是我目前使用的三步检查流程,在分享预览链接前必须完成:

第一步:检查构建日志

在部署上线前,先检查构建输出。在 Vercel 构建日志里搜索变量名。如果你在任何地方写了 console.log(process.env)——哪怕是测试文件——都会出现在构建输出里。

第二步:检查客户端 bundle

在浏览器里打开预览链接,进入 DevTools → Application → Local Storage。然后在 Sources 或 Network 面板中搜索包含 sk_livesk_testNEXT_PUBLIC_ 等前缀字符串的 .js 文件。一条简单的 grep 就能暴露问题:

curl -s https://your-preview.vercel.app/_next/static/chunks/*.js | grep -oE 'sk_live_[A-Za-z0-9]+'

第三步:用 secrets 扫描工具检查源码

gitleakstrufflehog 或 Vercel 的 vercel env ls 命令可以发现本不该被提交的变量。每次推送前,我都会在本地运行 gitleaks detect --source . --verbose

我发现了什么:完整泄漏清单

当我跑完上面的检查,Stripe 密钥只是最显眼的问题。我还发现了三个额外暴露:

变量 风险等级 泄漏方式
NEXT_PUBLIC_STRIPE_KEY 高(生产密钥) 硬编码在客户端代码读取的配置对象中
NEXT_PUBLIC_SENTRY_DSN 包含在错误追踪初始化代码中
NEXT_PUBLIC_API_URL 指向一个认证较弱的 staging 端点
AWS_ACCESS_KEY_ID 危急 误提交在 .env.local 中并被打包进构建输出

AWS 密钥是让我彻夜难眠的那一个——它挂了 AdministratorAccess,理由是”只是测试用的”。Vercel 构建流程把它复制进了构建输出,预览部署通过 source map 把它暴露出来。

如何修复 Vercel 环境变量泄漏

以下是我当时执行的修复流程,也是我们团队在Trove Deck Solution为每个客户交付 Vercel 托管应用时的标准步骤。

1. 严格区分公开变量和私有变量

Vercel 通过 NEXT_PUBLIC_ 前缀区分客户端变量和服务端变量。审计每个变量,把任何不需要在浏览器里运行的前缀去掉。只要变量包含密钥、令牌或任何敏感信息,就绝对不应该使用 NEXT_PUBLIC_ 前缀。

2. 创建分支级变量覆盖

在 Vercel Dashboard → Settings → Environment Variables 中,将每个变量的作用域设置为 Production Only。不要让 PreviewDevelopment 继承 production 密钥。预览构建使用独立的测试密钥(sk_test_...)。

3. 添加 pre-commit 钩子

用 Husky 安装 gitleaks 作为 pre-commit 钩子:

npm install --save-dev gitleaks husky
npx husky add .husky/pre-commit "gitleaks detect --source . --staged"

这能从源头阻止密钥进入 git 历史。

4. 立即轮换所有暴露过的密钥

如果密钥曾在预览部署中出现过,立即轮换。不要假设 URL “大概没人看到”。我在我发现泄漏后的一个小时内轮换了 Stripe 密钥、AWS 凭证和 Sentry DSN。

5. 给预览 URL 加上身份验证

Vercel 支持对预览部署启用密码保护和身份验证。在 Project Settings → Deployment Protection 中开启它。即使你修复了代码,这也是一层额外防护——任何拿到 URL 的人仍然无法访问应用。

泄漏的环境变量到底有多大风险?

预览部署中暴露的密钥与生产环境暴露的密钥风险完全相同。Stripe sk_live 密钥允许未授权扣款。权限过宽的 AWS 密钥允许创建资源、访问数据,甚至挖矿。根据 2024 年 Verizon 数据泄露调查报告,68% 的数据泄露涉及人为因素——包括配置错误和凭证暴露。你的预览 URL 就是带有 production 密钥的公开基础设施。

与需要 VPN 才能访问的 staging 服务器不同,Vercel 预览 URL 没有网络边界。任何拿到链接的人——或者能猜到可预测 slug 的人——都能获得应用提供的全部内容。

和其他托管平台相比情况如何?

Vercel 并不是唯一有这个问题的平台。Netlify、Railway 和 Render 都会创建带有环境变量继承的预览部署。区别在于 Vercel 的 NEXT_PUBLIC_ 约定制造了一种虚假的安全感——开发者以为这个前缀意味着”对公开安全”,实际上它只意味着”会被打包到客户端 bundle”。当你的客户端 bundle 从一个公开可访问的 URL 提供服务时,这个区别至关重要。

平台 默认预览认证 分支级变量覆盖 客户端密钥风险
Vercel 否(免费层) 过度使用 NEXT_PUBLIC_ 时很高
Netlify 客户端 JS 场景下很高
Railway 是(付费版) 较低(容器隔离)
Render 较低(默认服务端渲染)

现在应该做什么

如果你在 Vercel 上托管,今天就跑一遍三步检查——构建日志、客户端 bundle grep、源码扫描。不要等客户替你发现密钥。轮换所有暴露过的东西。给预览部署加上认证保护。审查每个项目中 NEXT_PUBLIC_ 前缀的使用情况。

如何防止未来预览部署再次泄漏?

预防清单很简单但需要纪律:永远不要在预览环境使用 production 密钥,每次提交前扫描代码,每个预览 URL 都加上密码保护。如果你的团队基于 Vercel 交付自定义软件,需要对部署流水线做一次结构化的安全审查,Trove Deck Solution可以帮忙——我们对每个客户部署都执行相同的审查流程。

#Vercel#EnvironmentVariables#DevOps#Security#IndieHackers#SaaS#WebDev#Privacy