全部文章

你的 Vercel 预览部署在泄露密钥吗?这样检查

开发者伸手触碰笔记本屏幕上发光的红色挂锁,暗色办公室背景

朗读全文

设想周四深夜,一条 Slack 消息来自正在看你预览链接的人:”你的 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_live、sk_test、NEXT_PUBLIC_ 等前缀字符串的 .js 文件。一条简单的 grep 就能暴露问题:

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

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

gitleaks、trufflehog 或 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 环境变量泄漏

以下是修复流程,一步一步来。

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

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

2. 创建分支级变量覆盖

在 Vercel Dashboard → Settings → Environment Variables 中,将每个变量的作用域设置为 Production Only。不要让 Preview 或 Development 继承 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可以帮忙。

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