SupabaseRPC审计技能Skill supabase-audit-rpc

这个技能用于发现和测试Supabase项目中暴露的PostgreSQL RPC函数,以识别安全漏洞如RLS绕过和SQL注入。适用于网络安全审计、渗透测试和API安全评估。关键词:Supabase, RPC, 审计, 安全, PostgreSQL, RLS, 渗透测试, SQL注入, API安全。

安全审计 0 次安装 0 次浏览 更新于 3/18/2026

名称: 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() ...

强制:渐进式上下文文件更新

⚠️ 此技能必须在执行过程中渐进式更新跟踪文件,而非仅在结束时。

关键规则:逐步写入

不要在结束时批量写入所有内容。相反:

  1. 测试每个函数前 → 记录动作到.sb-pentest-audit.log
  2. 每个函数分析后 → 立即更新.sb-pentest-context.json
  3. 发现每个漏洞后 → 立即记录发现

这确保如果技能被中断、崩溃或超时,所有截至该点的发现都被保存。

必需动作(渐进式)

  1. 更新.sb-pentest-context.json 包含结果:

    {
      "rpc_audit": {
        "timestamp": "...",
        "functions_found": 6,
        "summary": { "safe": 3, "p0_critical": 3 },
        "findings": [ ... ]
      }
    }
    
  2. 记录到.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已更新
    
  3. 如果文件不存在,在写入前创建它们。

未更新上下文文件是不可接受的。

强制:证据收集

📁 证据目录: .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 — 用户枚举测试