name: supabase-audit-rls description: 测试行级安全(RLS)策略以发现常见的绕过漏洞和错误配置。
RLS 策略审计
🔴 关键:需要渐进式文件更新
你必须在过程中写入上下文文件,而不仅仅在最后。
- 在每次发现后立即写入
.sb-pentest-context.json- 在每次测试前后记录到
.sb-pentest-audit.log- 不要等到技能完成才更新文件
- 如果技能崩溃或中断,所有先前的发现必须已经保存
这不是可选的。失败进行渐进式写入是一个关键错误。
这个技能测试行级安全(RLS)策略以发现常见漏洞和错误配置。
何时使用此技能
- 在发现表中数据暴露后
- 验证RLS策略是否正确实施
- 测试常见的RLS绕过技术
- 作为全面安全审计的一部分
先决条件
- 列出表
- 匿名密钥可用
- 最好也使用认证用户令牌进行测试
理解RLS
Supabase/PostgreSQL中的行级安全:
-- 在表上启用RLS
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- 创建策略
CREATE POLICY "用户查看自己的帖子"
ON posts FOR SELECT
USING (auth.uid() = author_id);
如果RLS已启用但无策略存在,所有访问都被阻止。
常见RLS问题
| 问题 | 描述 | 严重性 |
|---|---|---|
| RLS 禁用 | 表没有RLS保护 | P0 |
| 缺失策略 | RLS启用但无SELECT策略 | 可变 |
| 过于宽松 | 策略允许过多访问 | P0-P1 |
| 缺失操作 | 有SELECT策略但无INSERT/UPDATE/DELETE | P1 |
| USING vs WITH CHECK | 读取允许但写入不一致 | P1 |
测试向量
此技能测试这些常见的绕过场景:
1. 未认证访问
GET /rest/v1/users?select=*
# 无Authorization头或仅使用匿名密钥
2. 跨用户访问
# 作为用户A,尝试访问用户B的数据
GET /rest/v1/orders?user_id=eq.[用户B-ID]
Authorization: Bearer [用户A令牌]
3. 过滤器绕过
# 尝试使用OR条件绕过过滤器
GET /rest/v1/posts?or=(published.eq.true,published.eq.false)
4. 连接利用
# 尝试通过相关表访问数据
GET /rest/v1/comments?select=*,posts(*)
5. RPC绕过
# 检查RPC函数是否绕过RLS
POST /rest/v1/rpc/get_all_users
用法
基本RLS审计
审计我的Supabase项目上的RLS策略
特定表
测试users表上的RLS
使用认证用户
使用此用户令牌测试RLS策略: eyJ...
输出格式
═══════════════════════════════════════════════════════════
RLS 策略审计
═══════════════════════════════════════════════════════════
项目: abc123def.supabase.co
审计表数: 8
─────────────────────────────────────────────────────────
按表的RLS状态
─────────────────────────────────────────────────────────
1. users
RLS 启用: ❌ 否
状态: 🔴 P0 - 无RLS保护
所有操作无限制允许!
测试结果:
├── 匿名SELECT: ✓ 返回所有1,247行
├── 匿名INSERT: ✓ 成功(测试后回滚)
├── 匿名UPDATE: ✓ 将成功
└── 匿名DELETE: ✓ 将成功
立即修复:
```sql
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY "用户查看自己的数据"
ON users FOR ALL
USING (auth.uid() = id);
```
2. posts
RLS 启用: ✅ 是
找到策略: 2
状态: ✅ 正确配置
策略:
├── "公开查看已发布" (SELECT)
│ └── USING: (published = true)
└── "作者管理自己的" (ALL)
└── USING: (auth.uid() = author_id)
测试结果:
├── 匿名SELECT: 仅已发布帖子(正确)
├── 匿名INSERT: ❌ 阻止(正确)
├── 跨用户访问: ❌ 阻止(正确)
└── 过滤器绕过: ❌ 阻止(正确)
3. orders
RLS 启用: ✅ 是
找到策略: 1
状态: 🟠 P1 - 部分问题
策略:
└── "用户查看自己的订单" (SELECT)
└── USING: (auth.uid() = user_id)
发现问题:
├── 无INSERT策略 - 用户无法通过API创建订单
├── 无UPDATE策略 - 用户无法修改其订单
└── 这可能是故意的(通过Edge Functions处理订单)
建议: 如果故意,请记录,或添加策略:
```sql
CREATE POLICY "用户插入自己的订单"
ON orders FOR INSERT
WITH CHECK (auth.uid() = user_id);
```
4. comments
RLS 启用: ✅ 是
找到策略: 2
状态: 🟠 P1 - 可能绕过
策略:
├── "任何人都可以读取" (SELECT)
│ └── USING: (true) ← 过于宽松
└── "用户在帖子上评论" (INSERT)
└── WITH CHECK: (auth.uid() = user_id)
发现问题:
└── SELECT策略允许读取所有评论
包括user_id,使用户关联成为可能
建议:
```sql
-- 使用视图隐藏user_id
CREATE VIEW public.comments_public AS
SELECT id, post_id, content, created_at FROM comments;
```
5. settings
RLS 启用: ❌ 否
状态: 🔴 P0 - 无RLS保护
包含敏感配置!
需要立即行动。
─────────────────────────────────────────────────────────
摘要
─────────────────────────────────────────────────────────
RLS 禁用: 2 表 (users, settings) ← 关键
RLS 启用: 6 表
├── 正确配置: 3
├── 部分问题: 2
└── 主要问题: 1
绕过测试:
├── 未认证访问: 2 表易受攻击
├── 跨用户访问: 0 表易受攻击
├── 过滤器绕过: 0 表易受攻击
└── 连接利用: 1 表允许数据泄漏
═══════════════════════════════════════════════════════════
上下文输出
{
"rls_audit": {
"timestamp": "2025-01-31T10:45:00Z",
"tables_audited": 8,
"summary": {
"rls_disabled": 2,
"rls_enabled": 6,
"properly_configured": 3,
"partial_issues": 2,
"major_issues": 1
},
"findings": [
{
"table": "users",
"rls_enabled": false,
"severity": "P0",
"issue": "无RLS保护",
"operations_exposed": ["SELECT", "INSERT", "UPDATE", "DELETE"]
},
{
"table": "comments",
"rls_enabled": true,
"severity": "P1",
"issue": "SELECT策略过于宽松",
"detail": "user_id暴露,使用户关联成为可能"
}
]
}
}
常见RLS模式
好: 用户拥有自己的数据
CREATE POLICY "用户拥有自己的数据"
ON user_data FOR ALL
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
好: 公开读取,认证写入
-- 任何人都可以读取
CREATE POLICY "公开读取" ON posts
FOR SELECT USING (published = true);
-- 仅作者可以写入
CREATE POLICY "作者写入" ON posts
FOR INSERT WITH CHECK (auth.uid() = author_id);
CREATE POLICY "作者更新" ON posts
FOR UPDATE USING (auth.uid() = author_id);
坏: 使用 (true)
-- ❌ 过于宽松
CREATE POLICY "任何人" ON secrets
FOR SELECT USING (true);
坏: 忘记 WITH CHECK
-- ❌ 用户可以INSERT任何user_id
CREATE POLICY "插入" ON posts
FOR INSERT WITH CHECK (true); -- 应该检查user_id!
RLS 绕过文档
对于每个发现的绕过,技能提供:
- 漏洞描述
- 概念证明查询
- 影响评估
- 修复SQL代码
- 文档链接
强制: 渐进式上下文文件更新
⚠️ 此技能必须在执行过程中渐进式更新跟踪文件,而不仅仅在最后。
关键规则: 边走边写
不要将所有写入批处理到最后。相反:
- 在测试每个表之前 → 记录操作到
.sb-pentest-audit.log - 在每次RLS发现后 → 立即更新
.sb-pentest-context.json - 在每次测试完成后 → 记录结果到
.sb-pentest-audit.log
这确保如果技能中断、崩溃或超时,所有到该点的发现都被保存。
所需操作(渐进式)
-
更新
.sb-pentest-context.json与结果:{ "rls_audit": { "timestamp": "...", "tables_audited": 8, "summary": { "rls_disabled": 2, ... }, "findings": [ ... ] } } -
记录到
.sb-pentest-audit.log:[时间戳] [supabase-audit-rls] [开始] 审计RLS策略 [时间戳] [supabase-audit-rls] [发现] P0: users表无RLS [时间戳] [supabase-audit-rls] [上下文已更新] .sb-pentest-context.json已更新 -
如果文件不存在,在写入前创建它们。
失败更新上下文文件是不可接受的。
强制: 证据收集
📁 证据目录: .sb-pentest-evidence/03-api-audit/rls-tests/
要创建的证据文件
| 文件 | 内容 |
|---|---|
rls-tests/[表]-anon.json |
匿名访问测试结果 |
rls-tests/[表]-auth.json |
认证访问测试结果 |
rls-tests/cross-user-test.json |
跨用户访问尝试 |
证据格式(RLS绕过)
{
"evidence_id": "RLS-001",
"timestamp": "2025-01-31T10:25:00Z",
"category": "api-audit",
"type": "rls_test",
"severity": "P0",
"table": "users",
"rls_enabled": false,
"tests": [
{
"test_name": "anon_select",
"description": "匿名用户SELECT访问",
"request": {
"curl_command": "curl -s '$URL/rest/v1/users?select=*&limit=5' -H 'apikey: $ANON_KEY'"
},
"response": {
"status": 200,
"rows_returned": 5,
"total_accessible": 1247
},
"result": "易受攻击",
"impact": "所有用户数据无需认证即可访问"
},
{
"test_name": "anon_insert",
"description": "匿名用户INSERT访问",
"request": {
"curl_command": "curl -X POST '$URL/rest/v1/users' -H 'apikey: $ANON_KEY' -d '{...}'"
},
"response": {
"status": 201
},
"result": "易受攻击",
"impact": "可以创建任意用户记录"
}
],
"remediation_sql": "ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY \"用户查看自己的数据\" ON users FOR SELECT USING (auth.uid() = id);"
}
添加到 curl-commands.sh
# === RLS 绕过测试 ===
# 测试对users表的匿名访问
curl -s "$SUPABASE_URL/rest/v1/users?select=*&limit=5" \
-H "apikey: $ANON_KEY" -H "Authorization: Bearer $ANON_KEY"
# 测试过滤器绕过
curl -s "$SUPABASE_URL/rest/v1/posts?or=(published.eq.true,published.eq.false)" \
-H "apikey: $ANON_KEY"
相关技能
supabase-audit-tables-list— 首先列出表supabase-audit-tables-read— 查看实际数据暴露supabase-audit-rpc— RPC函数可能绕过RLSsupabase-report— 完整安全报告