00:00:00本月发布的一份报告对约 20,000 个独立应用进行了扫描,发现九分之一的程序
00:00:04在前台代码中泄露了 Supabase 的凭据。
00:00:08这些凭据不是出现在服务器日志或私有仓库里,
00:00:11而是直接存在于每个访客都会下载的 JavaScript 文件中。
00:00:14关键在于,这些应用并非被黑客入侵,而是开发者不小心把密钥打包进去了。
00:00:18需要明确的是,有些密钥确实应该是公开的。
00:00:21问题出在交付流程上,因为同样的错误可能会导致原本绝不该
00:00:25出现在浏览器中的密钥被发布出去。
00:00:27我们经常会发布新视频,
00:00:28请记得订阅。
00:00:35这里有个简单却容易让人困惑的事实。
00:00:38我们其实都心知肚明,但在快节奏开发时很容易忽略它。
00:00:41当你将环境变量标记为公开(public)时,构建工具会认为它属于
00:00:46浏览器端,并将其添加到客户端的代码包中。
00:00:50Next.js 通过 NEXT_PUBLIC 实现,Vite 通过 VITE_ 前缀,而 SvelteKit 则通过
00:00:56$env/static/public。
00:00:57“Next” 并不是什么安全标签,它其实是直接贴上去的“运输标签”。
00:01:01既然明白了这一点,再来看看 Supabase 的情况。
00:01:04有些密钥是公开的,比如 anon(匿名)或 publishable(可发布)密钥;有些则是私有的,比如
00:01:10service_role(服务角色)或 secret(秘密)密钥。
00:01:12如果私钥出现在浏览器中,后果你大概能猜到。
00:01:17在这种情况下,行级安全性(RLS)也救不了你。
00:01:20Supabase 的文档明确指出,服务密钥可以绕过 RLS。
00:01:24所以如果该密钥泄露到前端,你之前做的所有策略保护就全白费了。
00:01:29现在,让我用自己的沙盒环境向你演示这一切是如何发生的。
00:01:33首先,我用 Next.js 构建了一个简单的 CRUD 应用,并接入了 Supabase。
00:01:38这是我的 .env 文件,错误就在这里。
00:01:41我把一个密钥放在了带有 public 前缀的变量里。
00:01:45由于这个变量是可发布的,所以它出现在前端是预料之中的。
00:01:48真正的问题是,同样的 NEXT_PUBLIC 路径也可能由于疏忽而带出私钥。
00:01:53我运行了 npm run build 进行构建,然后启动了应用。
00:01:59现在我在 Chrome 浏览器里。
00:02:00让我给这个 CRUD 应用的数据库快速添加一些信息。
00:02:05好,现在我可以打开编译后的 JavaScript 包,搜索那个密钥。
00:02:10找到了。
00:02:12URL 和密钥就这么明晃晃地躺在用户下载到
00:02:18浏览器的文件里。
00:02:19请注意这里发生了什么(或者说没发生什么)。
00:02:21没有人入侵系统。
00:02:22我就这么直接找到了,对吧?
00:02:24没有任何漏洞利用。
00:02:25这只是在读取应用主动发布到互联网上的内容。
00:02:29既然我能看到,任何人都能看到。
00:02:32只需打开开发者工具,查看 JS 文件并搜索即可。
00:02:35就这么简单。
00:02:36如果你觉得“没人会去看”,那你就错了。互联网上到处是专门搜寻这些信息的活人和机器人。
00:02:41他们的工作就是盯着这些漏洞。
00:02:44那么,真正有效的修复方法如下:
00:02:47浏览器应该只调用你自己的 API。
00:02:50你的 API 运行在服务器端。
00:02:52那里才是私钥该待的地方。
00:02:54将私密操作移至 API 路由或服务器函数中。
00:02:58由客户端调用你的接口,再由接口去调用 Supabase。然后重新构建并检查代码包。
00:03:03如果代码包中不再有该密钥,才算真正修复了问题。但别就此止步,
00:03:04因为还有其他可以优化的点。
00:03:09确保为面向用户的表开启了行级安全性(RLS),
00:03:11并确认你的策略确实如你预期般运作。
00:03:16花点时间进行测试。
00:03:18我觉得这一步往往被大家忽略了。
00:03:19为了防止问题卷土重来——因为很多人修好一次后,
00:03:21又会在紧急开发时再次引入。我们需要一些防护栏。
00:03:26首先,在 CI 流程中加入敏感信息扫描,如果代码中出现了不该有的密钥,构建就应该失败。
00:03:28其次,建立 PR 审核规则,任何带有 NEXT_PUBLIC 或 VITE_ 的内容都默认视为公开,
00:03:34因为事实本就如此。
00:03:36最后,定期更换密钥。
00:03:41一旦你怀疑密钥可能泄露,立刻更换。
00:03:42这总比等上几天看会有什么后果要好得多。
00:03:43你现在就可以尝试以下操作:
00:03:47按照发布生产环境的方式构建你的应用。
00:03:50在输出结果中搜索 supabase、JWT、service_role 以及任何看起来像 Token 的内容。
00:03:52如果你发现了任何私密信息,请假设它已经泄露,因为既然你能搜到,别人也能。
00:03:55立即更换密钥,并修改服务器端的逻辑。
00:04:01如果看完视频你只能记住一句话,请记住:
00:04:05只要在代码包里,就是公开的。
00:04:08我们下期视频再见。
00:04:11If it's in the bundle, it's public.
00:04:13We'll see you in another video.