关于 supabase-audit-rls
supabase-audit-rls 是用于审查 Supabase Row Level Security(RLS)策略的安全审计技能,适合验证访问控制和排查常见绕过风险。它检查匿名及认证访问等场景,并要求逐步记录发现、测试结果和证据。
主要功能
- 01检查 RLS 是否启用及策略配置
- 02测试匿名与跨用户访问
- 03检查过滤器、关联查询和 RPC 绕过
- 04提供漏洞影响与 SQL 修复建议
- 05逐步保存审计上下文、日志和证据
适用场景
- 01审查 Supabase 项目表的 RLS
- 02验证用户只能访问获准的数据
- 03调查关联表或 RPC 的访问绕过
- 04生成可追溯的安全审计记录
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ yoanbernabeu/ supabase-pentest-skills --skill supabase-audit-rls安装后在 Claude Code 中输入/supabase-audit-rls确认已加载。
详细介绍
RLS 策略审计
🔴 严重:必须逐步更新文件
必须随执行进度写入上下文文件,而不是只在最后写入。
- 每发现一项问题,立即写入
.sb-pentest-context.json- 每次测试前后都要记录到
.sb-pentest-audit.log- 不要等到技能执行完成后才更新文件
- 如果技能崩溃或中断,所有此前发现的问题都必须已保存
此要求不可跳过。未能逐步写入属于严重错误。
此技能用于测试行级安全性(RLS)策略中的常见漏洞和配置错误。
何时使用此技能
- 在发现表中的数据暴露后
- 验证 RLS 策略是否正确实施
- 测试常见的 RLS 绕过技术
- 作为全面安全审计的一部分
前提条件
- 已列出表
- 已获取 Anon 密钥
- 最好也使用经过身份验证的用户令牌进行测试
了解 RLS
Supabase/PostgreSQL 中的行级安全性:
-- Enable RLS on a table
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Create a policy
CREATE POLICY "Users see own posts"
ON posts FOR SELECT
USING (auth.uid() = author_id);
如果启用了 RLS,但没有任何策略,则所有访问都会被阻止。
常见 RLS 问题
| 问题 | 说明 | 严重性 |
|---|---|---|
| 未启用 RLS | 表没有 RLS 保护 | P0 |
| 缺少策略 | 已启用 RLS,但没有 SELECT 策略 | 可变 |
| 权限过于宽松 | 策略允许过多访问 | P0-P1 |
| 缺少操作 | 有 SELECT 策略,但没有 INSERT/UPDATE/DELETE 策略 | P1 |
| USING 与 WITH CHECK | 允许读取,但写入规则不一致 | P1 |
测试向量
此技能会测试以下常见绕过场景:
1. 未经身份验证的访问
GET /rest/v1/users?select=*
# No Authorization header or with anon key only
2. 跨用户访问
# As user A, try to access user B's data
GET /rest/v1/orders?user_id=eq.[user-b-id]
Authorization: Bearer [user-a-token]
3. 绕过筛选条件
# Try to bypass filters with OR conditions
GET /rest/v1/posts?or=(published.eq.true,published.eq.false)
4. Join 漏洞利用
# Try to access data through related tables
GET /rest/v1/comments?select=*,posts(*)
5. RPC 绕过
# Check if RPC functions bypass RLS
POST /rest/v1/rpc/get_all_users
用法
基本 RLS 审计
Audit RLS policies on my Supabase project
指定表
Test RLS on the users table
使用经过身份验证的用户
Test RLS policies using this user token: eyJ...
输出格式
═══════════════════════════════════════════════════════════
RLS POLICY AUDIT
═══════════════════════════════════════════════════════════
Project: abc123def.supabase.co
Tables Audited: 8
─────────────────────────────────────────────────────────
RLS Status by Table
─────────────────────────────────────────────────────────
1. users
RLS Enabled: ❌ NO
Status: 🔴 P0 - NO RLS PROTECTION
All operations allowed without restriction!
Test Results:
├── Anon SELECT: ✓ Returns all 1,247 rows
├── Anon INSERT: ✓ Succeeds (tested with rollback)
├── Anon UPDATE: ✓ Would succeed
└── Anon DELETE: ✓ Would succeed
Immediate Fix:
```sql
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users see own data"
ON users FOR ALL
USING (auth.uid() = id);
```
2. posts
RLS Enabled: ✅ YES
Policies Found: 2
Status: ✅ PROPERLY CONFIGURED
Policies:
├── "Public sees published" (SELECT)
│ └── USING: (published = true)
└── "Authors manage own" (ALL)
└── USING: (auth.uid() = author_id)
Test Results:
├── Anon SELECT: Only published posts (correct)
├── Anon INSERT: ❌ Blocked (correct)
├── Cross-user access: ❌ Blocked (correct)
└── Filter bypass: ❌ Blocked (correct)
3. orders
RLS Enabled: ✅ YES
Policies Found: 1
Status: 🟠 P1 - PARTIAL ISSUE
Policies:
└── "Users see own orders" (SELECT)
└── USING: (auth.uid() = user_id)
Issue Found:
├── No INSERT policy - users can't create orders via API
├── No UPDATE policy - users can't modify their orders
└── This may be intentional (orders via Edge Functions)
Recommendation: Document if intentional, or add policies:
```sql
CREATE POLICY "Users insert own orders"
ON orders FOR INSERT
WITH CHECK (auth.uid() = user_id);
```
4. comments
RLS Enabled: ✅ YES
Policies Found: 2
Status: 🟠 P1 - BYPASS POSSIBLE
Policies:
├── "Anyone can read" (SELECT)
│ └── USING: (true) ← Too permissive
└── "Users comment on posts" (INSERT)
└── WITH CHECK: (auth.uid() = user_id)
Issue Found:
└── SELECT policy allows reading all comments
including user_id, enabling user correlation
Recommendation:
```sql
-- Use a view to hide user_id
CREATE VIEW public.comments_public AS
SELECT id, post_id, content, created_at FROM comments;
```
5. settings
RLS Enabled: ❌ NO
Status: 🔴 P0 - NO RLS PROTECTION
Contains sensitive configuration!
Immediate action required.
─────────────────────────────────────────────────────────
Summary
─────────────────────────────────────────────────────────
RLS Disabled: 2 tables (users, settings) ← CRITICAL
RLS Enabled: 6 tables
├── Properly Configured: 3
├── Partial Issues: 2
└── Major Issues: 1
Bypass Tests:
├── Unauthenticated access: 2 tables vulnerable
├── Cross-user access: 0 tables vulnerable
├── Filter bypass: 0 tables vulnerable
└── Join exploitation: 1 table allows data leakage
═══════════════════════════════════════════════════════════
上下文输出
{
"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": "No RLS protection",
"operations_exposed": ["SELECT", "INSERT", "UPDATE", "DELETE"]
},
{
"table": "comments",
"rls_enabled": true,
"severity": "P1",
"issue": "Overly permissive SELECT policy",
"detail": "user_id exposed enabling correlation"
}
]
}
}
常见 RLS 模式
良好:用户拥有自己的数据
CREATE POLICY "Users own their data"
ON user_data FOR ALL
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
良好:公开读取,经过身份验证后写入
-- Anyone can read
CREATE POLICY "Public read" ON posts
FOR SELECT USING (published = true);
-- Only authors can write
CREATE POLICY "Author write" ON posts
FOR INSERT WITH CHECK (auth.uid() = author_id);
CREATE POLICY "Author update" ON posts
FOR UPDATE USING (auth.uid() = author_id);
不良:使用 (true)
-- ❌ Too permissive
CREATE POLICY "Anyone" ON secrets
FOR SELECT USING (true);
不良:忘记 WITH CHECK
-- ❌ Users can INSERT any user_id
CREATE POLICY "Insert" ON posts
FOR INSERT WITH CHECK (true); -- Should check 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:[TIMESTAMP] [supabase-audit-rls] [START] Auditing RLS policies [TIMESTAMP] [supabase-audit-rls] [FINDING] P0: users table has no RLS [TIMESTAMP] [supabase-audit-rls] [CONTEXT_UPDATED] .sb-pentest-context.json updated -
如果文件不存在,请先创建再写入。
未能更新上下文文件不可接受。
强制要求:收集证据
📁 证据目录: .sb-pentest-evidence/03-api-audit/rls-tests/
需要创建的证据文件
| 文件 | 内容 |
|---|---|
rls-tests/[table]-anon.json | 匿名访问测试结果 |
rls-tests/[table]-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": "Anonymous user SELECT access",
"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": "VULNERABLE",
"impact": "All user data accessible without authentication"
},
{
"test_name": "anon_insert",
"description": "Anonymous user INSERT access",
"request": {
"curl_command": "curl -X POST '$URL/rest/v1/users' -H 'apikey: $ANON_KEY' -d '{...}'"
},
"response": {
"status": 201
},
"result": "VULNERABLE",
"impact": "Can create arbitrary user records"
}
],
"remediation_sql": "ALTER TABLE users ENABLE ROW LEVEL SECURITY;\nCREATE POLICY \"Users see own data\" ON users FOR SELECT USING (auth.uid() = id);"
}
添加到 curl-commands.sh
# === RLS BYPASS TESTS ===
# Test anon access to users table
curl -s "$SUPABASE_URL/rest/v1/users?select=*&limit=5" \
-H "apikey: $ANON_KEY" -H "Authorization: Bearer $ANON_KEY"
# Test filter bypass
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— 完整安全报告
评论
支持 Markdown