名称: supabase-audit-rpc 描述: 列出和测试暴露的PostgreSQL RPC函数,以检查安全问题和潜在RLS绕过。
RPC函数审计
🔴 关键:需要渐进式文件更新
您必须逐步写入上下文文件,而不是仅在结束时。
- 每个函数测试后立即写入
.sb-pentest-context.json- 每个函数测试前后记录到
.sb-pentest-audit.log- 不要等到技能完成才更新文件
- 如果技能崩溃或中断,所有先前发现必须已保存
这不是可选的。未逐步写入是严重错误。
此技能发现并测试通过Supabase的RPC端点暴露的PostgreSQL函数。
何时使用此技能
- 发现暴露的数据库函数
- 测试函数是否绕过RLS
- 检查函数参数中的SQL注入
- 作为综合API安全测试的一部分
先决条件
- Supabase URL和匿名密钥可用
- 表审计已完成(推荐)
理解Supabase RPC
Supabase通过以下方式暴露PostgreSQL函数:
POST https://[项目].supabase.co/rest/v1/rpc/[函数名称]
函数可以:
- ✅ 尊重RLS(如果使用
auth.uid()和适当安全) - ❌ 绕过RLS(如果是
SECURITY DEFINER而无检查) - ❌ 执行任意SQL(如果编写不当)
函数风险级别
| 类型 | 风险 | 描述 |
|---|---|---|
SECURITY INVOKER |
较低 | 以调用者权限运行 |
SECURITY DEFINER |
较高 | 以定义者权限运行 |
| 接受文本/json | 较高 | 潜在注入 |
| 返回setof | 较高 | 可返回多行 |
用法
基本RPC审计
审计我的Supabase项目上的RPC函数
测试特定函数
测试get_user_data RPC函数
输出格式
═══════════════════════════════════════════════════════════
RPC函数审计
═══════════════════════════════════════════════════════════
项目: abc123def.supabase.co
发现函数: 6
─────────────────────────────────────────────────────────
函数清单
─────────────────────────────────────────────────────────
1. get_user_profile(user_id uuid)
安全: INVOKER
返回: json
状态: ✅ 安全
分析:
├── 使用auth.uid()进行授权
├── 仅返回调用者个人资料
└── RLS被尊重
2. search_posts(query text)
安全: INVOKER
返回: posts集合
状态: ✅ 安全
分析:
├── 参数化查询(无注入)
├── RLS过滤结果
└── 仅返回已发布帖子
3. get_all_users()
安全: DEFINER
返回: users集合
状态: 🔴 P0 - RLS绕过
分析:
├── SECURITY DEFINER以所有者权限运行
├── 函数内无auth.uid()检查
├── 返回所有用户,无论调用者
└── 完全绕过RLS!
测试结果:
POST /rest/v1/rpc/get_all_users
→ 返回1,247条用户记录,含PII
立即修复:
```sql
-- 添加授权检查
CREATE OR REPLACE FUNCTION get_all_users()
RETURNS setof users
LANGUAGE sql
SECURITY INVOKER -- 改为INVOKER
AS $$
SELECT * FROM users
WHERE auth.uid() = id; -- 添加类似RLS的检查
$$;
```
4. admin_delete_user(target_id uuid)
安全: DEFINER
返回: void
状态: 🔴 P0 - 严重漏洞
分析:
├── SECURITY DEFINER具有删除能力
├── 无角色检查(匿名用户可调用!)
├── 可删除任何用户
└── 无审计跟踪
测试结果:
POST /rest/v1/rpc/admin_delete_user
体: {"target_id": "any-uuid"}
→ 函数对匿名用户可访问!
立即修复:
```sql
CREATE OR REPLACE FUNCTION admin_delete_user(target_id uuid)
RETURNS void
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
BEGIN
-- 添加角色检查
IF NOT (SELECT is_admin FROM profiles WHERE id = auth.uid()) THEN
RAISE EXCEPTION '未授权';
END IF;
DELETE FROM users WHERE id = target_id;
END;
$$;
-- 或更好:限制仅对认证用户
REVOKE EXECUTE ON FUNCTION admin_delete_user FROM anon;
```
5. dynamic_query(table_name text, conditions text)
安全: DEFINER
返回: json
状态: 🔴 P0 - SQL注入
分析:
├── 接受原始文本参数
├── 可能拼接为查询
├── SQL注入可能
测试结果:
POST /rest/v1/rpc/dynamic_query
体: {"table_name": "users; DROP TABLE users;--", "conditions": "1=1"}
→ 注入向量确认!
立即操作:
→ 立即删除此函数
```sql
DROP FUNCTION IF EXISTS dynamic_query;
```
切勿从用户输入构建查询。使用参数化查询。
6. calculate_total(order_id uuid)
安全: INVOKER
返回: 数值
状态: ✅ 安全
分析:
├── UUID参数(类型安全)
├── SECURITY INVOKER尊重RLS
└── 仅访问调用者订单
─────────────────────────────────────────────────────────
总结
─────────────────────────────────────────────────────────
总函数数: 6
安全: 3
P0严重: 3
├── get_all_users (RLS绕过)
├── admin_delete_user (无授权检查)
└── dynamic_query (SQL注入)
优先操作:
1. 立即删除dynamic_query函数
2. 向admin_delete_user添加授权检查
3. 修复get_all_users以尊重RLS
═══════════════════════════════════════════════════════════
注入测试
此技能测试文本/varchar参数中的SQL注入:
安全(参数化)
-- ✅ 安全: 使用参数占位符
CREATE FUNCTION search_posts(query text)
RETURNS setof posts
AS $$
SELECT * FROM posts WHERE title ILIKE '%' || query || '%';
$$ LANGUAGE sql;
易受攻击(拼接)
-- ❌ 易受攻击: 动态SQL执行
CREATE FUNCTION dynamic_query(tbl text, cond text)
RETURNS json
AS $$
DECLARE result json;
BEGIN
EXECUTE format('SELECT json_agg(t) FROM %I t WHERE %s', tbl, cond)
INTO result;
RETURN result;
END;
$$ LANGUAGE plpgsql;
上下文输出
{
"rpc_audit": {
"timestamp": "2025-01-31T11:00:00Z",
"functions_found": 6,
"summary": {
"safe": 3,
"p0_critical": 3,
"p1_high": 0
},
"findings": [
{
"function": "get_all_users",
"severity": "P0",
"issue": "通过SECURITY DEFINER绕过RLS",
"impact": "所有用户数据可访问",
"remediation": "改为SECURITY INVOKER或添加授权检查"
},
{
"function": "dynamic_query",
"severity": "P0",
"issue": "SQL注入漏洞",
"impact": "可能执行任意SQL",
"remediation": "删除函数,使用参数化查询"
}
]
}
}
RPC函数最佳实践
1. 优选SECURITY INVOKER
CREATE FUNCTION my_function()
RETURNS ...
SECURITY INVOKER -- 尊重RLS
AS $$ ... $$;
2. 始终检查auth.uid()
CREATE FUNCTION get_my_data()
RETURNS json
AS $$
SELECT json_agg(d) FROM data d
WHERE d.user_id = auth.uid(); -- 始终按调用者过滤
$$ LANGUAGE sql SECURITY INVOKER;
3. 使用REVOKE用于敏感函数
-- 移除匿名访问
REVOKE EXECUTE ON FUNCTION admin_function FROM anon;
-- 仅认证用户
GRANT EXECUTE ON FUNCTION admin_function TO authenticated;
4. 避免文本参数用于动态查询
-- ❌ 坏
CREATE FUNCTION query(tbl text) ...
-- ✅ 好: 对每个表使用特定函数
CREATE FUNCTION get_users() ...
CREATE FUNCTION get_posts() ...
强制:渐进式上下文文件更新
⚠️ 此技能必须在执行过程中渐进式更新跟踪文件,而非仅在结束时。
关键规则:逐步写入
不要在结束时批量写入所有内容。相反:
- 测试每个函数前 → 记录动作到
.sb-pentest-audit.log - 每个函数分析后 → 立即更新
.sb-pentest-context.json - 发现每个漏洞后 → 立即记录发现
这确保如果技能被中断、崩溃或超时,所有截至该点的发现都被保存。
必需动作(渐进式)
-
更新
.sb-pentest-context.json包含结果:{ "rpc_audit": { "timestamp": "...", "functions_found": 6, "summary": { "safe": 3, "p0_critical": 3 }, "findings": [ ... ] } } -
记录到
.sb-pentest-audit.log:[TIMESTAMP] [supabase-audit-rpc] [START] 审计RPC函数 [TIMESTAMP] [supabase-audit-rpc] [FINDING] P0: dynamic_query有SQL注入 [TIMESTAMP] [supabase-audit-rpc] [CONTEXT_UPDATED] .sb-pentest-context.json已更新 -
如果文件不存在,在写入前创建它们。
未更新上下文文件是不可接受的。
强制:证据收集
📁 证据目录: .sb-pentest-evidence/03-api-audit/rpc-tests/
创建证据文件
| 文件 | 内容 |
|---|---|
function-list.json |
所有发现的RPC函数 |
vulnerable-functions/[名称].json |
每个易受攻击函数的详细信息 |
证据格式(易受攻击函数)
{
"evidence_id": "RPC-001",
"timestamp": "2025-01-31T10:30:00Z",
"category": "api-audit",
"type": "rpc_vulnerability",
"severity": "P0",
"function": "get_all_users",
"analysis": {
"security_definer": true,
"auth_check": false,
"rls_bypass": true
},
"test": {
"request": {
"method": "POST",
"url": "https://abc123def.supabase.co/rest/v1/rpc/get_all_users",
"curl_command": "curl -X POST '$URL/rest/v1/rpc/get_all_users' -H 'apikey: $ANON_KEY' -H 'Content-Type: application/json'"
},
"response": {
"status": 200,
"rows_returned": 1247,
"sample_data": "[已编辑 - 包含用户PII]"
}
},
"impact": "绕过RLS,返回所有1,247条用户记录",
"remediation": "改为SECURITY INVOKER或添加auth.uid()检查"
}
添加到curl-commands.sh
# === RPC函数测试 ===
# 测试get_all_users函数(如果可访问则P0)
curl -X POST "$SUPABASE_URL/rest/v1/rpc/get_all_users" \
-H "apikey: $ANON_KEY" \
-H "Content-Type: application/json"
# 测试admin_delete_user函数
curl -X POST "$SUPABASE_URL/rest/v1/rpc/admin_delete_user" \
-H "apikey: $ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"target_id": "test-uuid"}'
相关技能
supabase-audit-tables-list— 列出暴露的表supabase-audit-rls— 测试RLS策略supabase-audit-auth-users— 用户枚举测试