Lovable 上生产安全吗:访问控制审查问题
Lovable 很适合快速原型,但生产系统的问题不是能不能跑,而是谁能审查访问控制。RLS、前端过滤和安全扫描都重要;真正的边界必须在服务端可见、可强制、可签字。
TL;DR: Lovable 能把原型和早期产品做得很快,这一点值得承认。风险在于:决定「谁能读到用户数据」的访问控制,常常落到 Supabase RLS、SQL 策略和生成代码里,而负责上线的人未必能审查这些边界。前端过滤 .eq('owner', user.id) 能让 demo 表现正确,但它只是请求层面的便利,不是安全边界。公开披露的 CVE-2025-48757 和相关扫描报告,说明错误 RLS 配置确实能导致真实数据暴露。Lovable 后续加入了安全扫描和企业控制,这些有价值;但扫描器降低风险,不等于把生产责任转移出去。「能跑」和「安全」仍然是两个不同的主张。
让我把这个失败的确切形状展示给你看,因为「vibe coding 有风险」这句话毫无用处,关键全在细节里。
一位创始人描述了一个带用户账户的应用。Lovable 把它做出来,接上 Supabase,然后上线。每个用户只看到自己的记录。创始人用两个账户测试;表现完美。在 demo 里,它是正确的。
现在打开浏览器的网络面板——那个非开发者从不会打开的东西——看看应用实际发出的请求:
// 前端把查询「限定」到当前用户:
const { data } = await supabase
.from('customers')
.select('*')
.eq('owner', user.id) // 看起来像是安全边界。它不是。
那个 .eq('owner', user.id) 是一个过滤条件——一个请求上的参数。任何人都可以打开网络面板,复制那个端点,然后去掉过滤条件重新发出去。本该阻止他们拿到所有人那些行的,是表上的行级安全策略。公开漏洞案例里的错误配置,常常长这样:
-- 本应充当边界的那条 RLS 策略:
create policy "Enable read access for all users"
on public.customers for select
using (true); -- ← 对每一行、每一个调用者都求值为 true
using (true) 的意思是:每一个已认证的调用者都可以读到每一行。 这条策略存在。它是开着的。而它什么都不保护。这个应用只在恰好一种配置下看起来安全——前端碰巧请求的那一种——而在任何人换一种问法的那一刻就敞开了。这不是抽象担忧;公开披露的 CVE-2025-48757 讨论的正是这类 RLS 与客户端请求边界混淆的问题。
过滤不是边界
这就是整个概念,值得把它点名出来,因为你一旦看见就再也无法当作没看见:过滤是请求层面的便利;边界是无论请求如何、都在服务端被强制执行的东西。 demo 能跑,靠的是过滤。数据安全,只有在存在边界时才成立。两者从外部看是无法区分的——它们都让两个测试账户看到正确的东西——而这恰恰是为什么一个非开发者无法靠使用一个应用来分辨它是安全的还是已暴露的。
被 Lovable 赋予构建应用之力的那个人,按照产品自身的设计,正是最没有装备去读懂 using (true) 与真正的 using (auth.uid() = owner) 之间差别的那个人。能力和理解被拆开了。这个拆分,既是产品的魔法,又在同一笔之下是它的责任。
拆解那个显而易见的反驳:「Lovable 加了一个安全扫描器」
平心而论,这个批评击中了 Lovable,他们也作出了回应——一个安全扫描器,一个带 SSO 和审计日志的企业层。所以公正的问题是:这难道没把口子堵上吗?
看看扫描器能解决什么、不能解决什么。Lovable 的安全扫描会检查数据库配置、RLS 规则、云项目设置和常见误配置,这当然有价值,也比没有强得多。但检查是否存在风险信号和证明每条访问路径都符合业务权限不是一回事。后者需要理解数据模型、角色、记录归属和异常路径。扫描器可以帮你发现问题、降低漏检概率,却不能替你承担「这套访问控制已经可以上生产」的签字责任。
而那些企业级控制——SSO、组织审计日志——管的是人们如何登录 Lovable,不是AI 在你应用里写了什么 RLS。它们是真实的、好的,而且与此正交。缺口不在 Lovable 的企业姿态。缺口在于:AI 为每个应用生成的那套访问逻辑,对那个唯一为它负责的人来说是读不懂的。
解释 170 起泄露的那个数字:验证的不对称
这里真正值得记住的,不是某个具体数字,而是验证成本的不对称。比较两项成本:
- 构建成本(把应用做出来):一句话。按设计,实际上是零——这正是整个卖点。
- 验证成本(确认它是安全的):读每张表生成的 RLS,对每一条 API 路径进行推理,把过滤与边界区分开。这是专家工时,而且是构建者并不具备的一项技能。
当构建成本坍缩到几分钟,而验证成本仍停留在专家工时时,一个非专家很容易跳过验证——因为他做不了,而应用看起来已经完成了。把这个模式放大到一批应用里,你得到的就不只是几起偶发泄露,而是一种结构性风险。问题不只属于 Lovable;任何一个让非专家生成自己读不懂的访问控制的工具,都继承同样的不对称。
Lovable 恰恰正确的地方——以及那句诚实的限定
对于一个原型、一个落地页、一个黑客松作品、一个内部用完即弃的东西、一个融资 demo——任何没有真实人物的数据坐在应用背后的场景——Lovable 都很出色,而且几乎肯定比任何更讲规矩的东西(包括我们)都快。当没有任何东西需要被暴露时,验证的不对称不花你一分钱。用它,并享受它。
还有那句让这篇文章不至于成为一篇抹黑稿的诚实限定:这是结构性的,不是 Lovable 的缺陷。任何一个为非专家坍缩了构建成本、却把验证留在专家成本上的工具,都继承同样的不对称——而 Lovable 正在积极地试图收窄它。要点不是「Lovable 不好」。要点是:对于生产数据,「构建者读不懂访问控制」这件事就足以让它出局,无论构建器上印的是谁的标志。
这条线是鲜明的:在真实人物的数据开始活在它背后的那一刻——客户、患者、员工——「能跑」就不再是任何东西的证据,「我看不见那条边界」也就不再是一种便利,而成了那个风险本身。
一个你三十秒就能做的测试
忘掉功能清单。去问那个把它上线的人:
「打开网络面板,拿到那条数据请求,去掉过滤条件,然后把它发出去。你拿回来的是只有你自己的那些行——还是所有人的?」
如果他做不了这个测试,或者看不懂它,或者答案是「所有人的」,那么这个应用至少还没有完成生产级安全审查——无论它看起来多么精致,无论某个扫描器给它打了个对勾。
ObjectStack 的立场
ObjectStack 把那个导致泄露的拆分反转过来。访问控制不是构建者读不懂的生成 SQL;它是被声明出来、可读的元数据——作为对象一个属性的 read: owner == current_user——而且它是一条运行时在服务端强制执行的边界,所以去掉一个前端过滤什么都改变不了。当 AI 提议改动「谁能读到什么」时,它会作为一份非专家真的能批准的 diff 浮现出来(「这会让 customers 对所有人可读——确认吗?」),而绝不会是一个你本得读 SQL 才能抓到的、悄无声息的 using (true)。而且因为它是可自托管的、并坐落在你本来就在运行的系统上,敏感数据不会悄悄落到某个你无法检视的地方。
我们不会在「到第一个 demo 的时间」上赢过 Lovable,而对于原型我们也不会去试。真正要紧的主张,是在真实数据一旦牵涉其中的那一刻才生效的:一个 AI 构建的应用,它的安全边界必须对那个为它担责的人是可见的。看不见边界,不等于边界不存在;但在生产系统里,它意味着你还没有完成可签字的审查。