1/9 的 App 泄露了 Supabase 密钥:背后的原因竟然是这个

BBetter Stack
컴퓨터/소프트웨어창업/스타트업AI/미래기술

Transcript

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.

Key Takeaway

只要私密凭据被包含在前端代码包(Bundle)中,它就是公开且不安全的,开发者必须将私有逻辑移至服务端处理。

Highlights

约九分之一的受测应用在前端代码中泄露了 Supabase 凭据

泄露的核心原因在于开发者错误地使用了带有 public 前缀的环境变量

私有的 service_role 密钥一旦泄露,可以完全绕过行级安全性(RLS)保护

互联网上的机器人和恶意攻击者会持续扫描 JavaScript 代码包以搜寻敏感信息

真正的修复方案是将私密操作移至服务器端 API,而不是仅仅依赖 RLS

建议在 CI 流程中加入敏感信息扫描并定期更换泄露的密钥

Timeline

大规模密钥泄露现状

最近的一份研究报告指出,在对 20,000 个独立应用进行扫描后,发现约 11% 的程序在前端代码中泄露了 Supabase 凭据。这些密钥并非通过黑客入侵或服务器日志泄露,而是直接存在于用户浏览器下载的 JavaScript 文件中。视频强调,问题的根源在于开发者的交付流程错误,将本不该公开的密钥打包进了客户端。即使有些密钥设计上是可公开的,但流程漏洞会导致更严重的私钥外泄。这一现状提醒开发者,代码的安全性往往毁于不经意的打包配置。

环境变量与构建工具的工作原理

视频深入探讨了现代前端框架如 Next.js、Vite 和 SvelteKit 如何处理环境变量。当开发者为变量添加 NEXT_PUBLIC 或 VITE_ 等前缀时,构建工具会自动将其视为浏览器端可访问的内容。这些前缀本质上是“运输标签”,指示系统在打包时将对应的值嵌入到客户端代码中。在快节奏的开发环境中,这种机制极易被忽视,导致敏感信息被误标。了解这些框架的默认行为是防止信息泄露的第一步。开发者必须意识到,标记为公开的变量最终会以明文形式出现在互联网上。

Supabase 密钥类型与安全风险

Supabase 提供不同等级的密钥,包括可公开的 anon 密钥和权限极大的 service_role 私钥。如果 service_role 密钥泄露到前端,后果将是灾难性的,因为它拥有绕过所有行级安全性(RLS)策略的权限。这意味着攻击者可以利用该密钥直接访问、修改或删除数据库中的任何数据,让之前的安全设置形同虚设。视频指出,很多开发者误以为开启了 RLS 就能高枕无忧,却忽略了服务级密钥的越权风险。确保私钥留在服务器端是保障数据库安全的底线。

实战演示:密钥是如何被轻易发现的

作者通过一个 Next.js 的 CRUD 应用沙盒环境,演示了泄露发生的具体过程。在 .env 文件中,他故意将一个私密密钥放在了带有 NEXT_PUBLIC 前缀的变量里。构建并运行应用后,只需在 Chrome 开发者工具中搜索对应的关键字,就能直接看到明晃晃的密钥和数据库 URL。作者强调,这甚至算不上“黑客攻击”,仅仅是读取应用主动公开的信息。他警告说,网络上充满了专门搜寻此类泄露信息的自动化机器人,心存侥幸是极其危险的。

修复方案:从架构到流程的优化

针对泄露问题,视频给出了多层次的修复方案:首先应将所有私密操作移至 API 路由或服务器函数(Server Functions)中,让客户端只调用自己的接口。其次,开发者需要重新构建应用并搜索关键词,确认代码包中不再含有敏感字符串。在流程上,建议在 CI 持续集成中加入扫描工具,并建立严格的 PR 审核机制来检查公开变量。此外,一旦发现泄露嫌疑,应立即更换密钥,绝不拖延。作者最后总结道:“只要在代码包里,就是公开的”,这是每个开发者都必须铭记的安全准则。

Community Posts

No posts yet. Be the first to write about this video!

Write about this video