eugenicum/supabase-audit
GitHub: eugenicum/supabase-audit
专为 Supabase 生态设计的黑盒渗透测试与架构审计工具,通过 24 个阶段的实战化攻击模拟,主动挖掘并验证 RLS 绕过、越权访问等安全配置缺陷。
Stars: 0 | Forks: 0
# supabase-audit
[](https://opensource.org/licenses/MIT)
[](https://www.python.org/downloads/)
[](https://supabase.com)
**`supabase-audit`** 是一个针对 Supabase (PostgreSQL + PostgREST + GoTrue + Realtime + Storage + pg_graphql + Edge Functions) 的 24 阶段渗透测试与架构审计工具。它将你的项目视为一个黑盒,像真实的攻击者那样攻击每一个公共接口,并生成一份包含漏洞复现、修复方案和严重程度评级的 Markdown 报告。
开发这个工具是因为 Supabase 控制台只会告诉你_配置了什么_,而不会告诉你攻击者_实际上_能访问什么。
## 要求
- **Python 3.10+**(推荐 3.12)
- **一个你拥有的 Supabase 项目**(免费或付费计划——均支持)
- 从 Supabase 控制台获取的 **3 个凭证**(见下方第 2 步的说明)
- macOS / Linux / WSL(未经 Windows 原生环境测试)
## 安装
```
git clone https://github.com/eugenicum/supabase-audit.git
cd supabase-audit
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
```
这就是完整的安装过程。不需要系统级软件包、不需要 Docker、不需要数据库驱动。
## 设置 —— 哪里需要填写什么内容
审计程序会从 **2 个文件** 中读取信息:
| 文件 | 填写内容 | 存放位置 |
|---|---|---|
| **env 文件** (`.env` 风格) | 你的 3 个 Supabase 凭证(URL + 2 个密钥) | 代码库之外的任何位置。建议存放在:`~/.supabase-audit/myproject.env` |
| **项目配置** (`projects/.local.json`) | 审计设置 + env 文件的路径 | 代码库内部(`.local.json` 后缀已被 gitignore 忽略) |
### 第 1 步 — 创建你的 env 文件
将该文件保存在 `~/.supabase-audit/myproject.env`(如需要请先创建该目录)。将值替换为你真实的凭证:
```
SUPABASE_URL=https://.supabase.co
SUPABASE_ANON_KEY=eyJhbGc...your-anon-key...
SUPABASE_SERVICE_KEY=eyJhbGc...your-service-role-key...
# 可选 — 仅用于付费计划检查(PITR,通过 Management API 实现的 branches)
SUPABASE_ACCESS_TOKEN=sbp_...
```
锁定权限:`chmod 600 ~/.supabase-audit/myproject.env`
### 第 2 步 — 从 Supabase 控制台获取这 3 个值
在你的 Supabase 控制台 → **Project Settings → API** 中:
| 字段 | 从哪里复制 | 形式 |
|---|---|---|
| `SUPABASE_URL` | "Project URL" 输入框 | `https://abcdefghij.supabase.co` |
| `SUPABASE_ANON_KEY` | "Project API keys" → 标记为 `anon` `public` 的那一行 | 以 `eyJhbGc...` 开头 |
| `SUPABASE_SERVICE_KEY` | "Project API keys" → 标记为 `service_role` `secret` 的那一行(点击 "Reveal") | 以 `eyJhbGc...` 开头 |
**可选的 `SUPABASE_ACCESS_TOKEN`** — 在 https://supabase.com/dashboard/account/tokens 生成。仅在你需要进行 PITR / 备份 / 分支检查时(付费计划)才需要。否则可跳过。
### 第 3 步 — 创建你的项目配置
```
cp projects/_template.json projects/myproject.local.json
```
编辑 `projects/myproject.local.json` —— 必填字段标有星号:
```
{
"name": "myproject", // *
"env_file": "/Users/you/.supabase-audit/myproject.env", // * absolute path
"skip_tables": [],
"skip_table_prefixes": ["pg_", "geometry_", "geography_"],
"skip_rpcs": [],
"team_table": null,
"team_member_col": null,
"team_resource_col": null,
"allowlist": []
}
```
必填:
- `name` — 用于报告文件名
- `env_file` — 第 1 步中创建的文件的绝对路径
可选:
- `skip_tables` / `skip_rpcs` — 要忽略的名称(例如,针对 PostGIS 设置为 `["spatial_ref_sys"]`)
- `skip_table_prefixes` — 通常保持默认即可
- `team_table` / `team_member_col` / `team_resource_col` — 如果你的应用具有团队/组织结构,这会启用租户边界测试。例如针对 `team_members(user_id, team_id)` 表的配置:
"team_table": "team_members",
"team_member_col": "user_id",
"team_resource_col": "team_id"
- `allowlist` — 标记为已知安全对象的暴露面模式(参见下方的[白名单](#allowlisting-validated-safe-findings))
### 第 4 步 — 安装静态检查辅助工具(一次性操作,可选)
这会启用针对数据库 Schema 的 CVE 模式扫描(DEFINER 卫生状况、缺失 WITH CHECK 等)。如跳过,这些检查将干净地输出 `SKIP`。
```
cat install_static_helper.sql | pbcopy
# 粘贴到 Supabase Dashboard → SQL Editor → Run
```
它会创建一个只能由 `service_role` 调用的 `SECURITY DEFINER` 函数(`public._audit_static_query`)。你可以随时删除它:
```
DROP FUNCTION IF EXISTS public._audit_static_query(text);
```
## 运行
```
python supabase_audit.py --project myproject.local
```
审计大约需要 1–5 分钟,具体取决于项目大小。输出内容:
- 位于 `reports/myproject-.md` 的 Markdown 报告,包含复现步骤和修复建议
- 结尾处的横幅汇总了各种严重程度的数量
- 详细的逐探测项日志(每个测试,无论通过或失败)
## 测试内容
### 渗透测试探测(20 个阶段)
| 阶段 | 测试内容 |
|---|---|
| `attack_rls` | 针对每张表的 Anon + 跨租户 SELECT/INSERT/UPDATE/DELETE;结合 service-role 的真实基准进行交叉验证 |
| `attack_rpc` | Anon + user_b 调用所有 RPC;通过 `user_id` 参数测试 IDOR;标记特权命名模式 |
| `attack_storage` | 每个存储桶的 Anon 列举、Anon 下载、公开标志审查 |
| `attack_auth` | 注册频率限制,密码重置用户枚举,JWT 重放,OpenAPI Schema 暴露 |
| `attack_postgrest_advanced` | 外键遍历泄漏 (`?select=*,joined(*)`),隐藏列读取,`Content-Range` 计数预言机 |
| `attack_graphql` | 通过 pg_graphql 进行 `/graphql/v1` 内省 + 敏感类型查询 |
| `attack_realtime` | 在每个已发布的表上进行 Anon WebSocket 连接 |
| `attack_realtime_payload` | Service-role INSERT + Anon 监听 —— 确认 RLS 是否阻止了广播 Payload |
| `attack_edge_functions` | 针对近 `/functions/v1/` 的字典发现 + 认证绕过 |
| `attack_role_escalation` | 使用特权列(`role=admin`, `is_admin=true`, `credits=999999`)PATCH 自有行;软删除绕过 |
| `attack_storage_advanced` | 路径遍历 (`../etc-passwd`),URL 编码的 `%2F..%2F`,图片转换 DoS,签名 URL 篡改 |
| `attack_auth_advanced` | 匿名认证特性检查;OAuth `redirect_to` 白名单绕过 |
| `attack_extensions` | 通过带有 URL 参数的 RPC 进行 pg_net SSRF;vault/解密机密暴露 |
| `attack_recon` | CORS 反射,server/version 请求头,PG 版本横幅,健康检查端点 |
| `attack_jwt_forge` | 针对近 /auth/v1/user 使用 `alg: none` + 空签名 JWT |
| `attack_tenant_matrix` | 通过 `team_table`/`team_resource_col` 进行多团队边界测试 |
| `attack_audit_integrity` | 对类似审计表的表(`audit_events`, `*_log`, `*_history`)尝试 INSERT/DELETE |
| `attack_logic_flows` | 大规模赋值(未知列),并发相同邮箱注册竞态 |
| `attack_data_hygiene` | 错误响应中的 PII 正则扫描;加密列的明文启发式检查;存储桶策略完整性 |
| `attack_misc` | PgBouncer 可达性 (5432/6543),PostgREST 操作符滥用,分支检测 |
### 架构审计(4 个阶段)
| 阶段 | 测试内容 |
|---|---|
| `arch_rls_consistency` | 启用了 RLS 但策略数量为 **0** 的表;所有者列命名漂移;策略模式分布 |
| `arch_schema_design` | 没有主键的表;缺少支撑索引的外键;RLS 未过滤 `deleted_at IS NULL` 的软删除列 |
| `arch_dr_compliance` | 计划检测(免费版与付费版),备份可见性,GDPR 数据导出 RPC 的存在性,用户删除 RPC 的存在性,审计日志表的存在性 |
| `arch_operational` | 存在 `auth.users` 插入触发器;每张表都有 `updated_at` 触发器;孤立的触发器函数;pg_cron 作业列表 |
### Schema 级别的静态检查
由设置第 4 步中可选的 `SECURITY DEFINER` 辅助工具提供支持:
- 缺少 `SET search_path` 的 `SECURITY DEFINER` 函数(CVE 级别)
- 未设置 `security_invoker = on` 的视图(RLS 绕过)
- 缺少 `WITH CHECK` 的 UPDATE 策略(允许用户将行转移给其他所有者)
- RLS 处于 DISABLED 状态的 public 模式下的表
- 包含敏感表(`auth.*`, `vault.*`)的 `supabase_realtime` 发布
- 已安装的特权扩展(`pg_net`, `http`, `plpython3u`)
## 为什么会存在这个工具
Supabase 非常出色,但它的默认设置为你做了许多决定:
- `public` 模式中的新表会暴露给 PostgREST。
- `anon` 和 `authenticated` 角色具有广泛的功能级授权。
- Realtime 默认发布每一张公开表。
- pg_graphql 内省默认开启。
- 存储策略使用 `(storage.foldername(name))[1]` —— 很容易被误用。
每个默认设置单独来看都是合理的。但它们加在一起,就创造了一些你并未明确选择的攻击面。**`supabase-audit` 能找出它们。**
它还能捕捉到黑盒探测无法看到的东西 —— 使用 service-role 辅助工具检查 `pg_proc`、`pg_policy`、`pg_class`,寻找 CVE 模式的配置错误。
## 输出示例
```
============================================================
REPORT: reports/myproject-20260507T182229Z.md
CRITICAL=0 HIGH=0 MEDIUM=37 LOW=2
============================================================
```
每个发现项类似于:
```
### [严重] bucket temp-audio 接受 Path traversal
**Surface:** `bucket:temp-audio`
**What happened:** user_b uploaded with crafted path
`shares//../../etc-passwd` — storage policy didn't sanitize.
**Reproduction:**
curl -X POST 'https://...supabase.co/storage/v1/object/temp-audio/...'
-H 'Authorization: Bearer ' -d 'probe'
**Response excerpt:**
{'Key': 'temp-audio/etc-passwd', 'Id': '...'}
**Fix:** Add a per-user folder constraint to the INSERT policy:
WITH CHECK ((storage.foldername(name))[1] = auth.uid()::text)
```
## 常用参数
```
# 仅重新测试上次报告的 surfaces(快速)
python supabase_audit.py --project myproject --retest
# 与特定的较旧报告进行比较
python supabase_audit.py --project myproject --retest reports/myproject-2026-01-01.md
# 仅运行特定 phases
python supabase_audit.py --project myproject --only attack_rls attack_storage
# 将 RLS 攻击限制为特定 table
python supabase_audit.py --project myproject --only-tables contacts orders
# 将 RPC 攻击限制为特定 functions
python supabase_audit.py --project myproject --only-rpcs get_user_stats
# 仅 enumerate — 无攻击,无测试用户
python supabase_audit.py --project myproject --dry-run
```
## 将已验证安全的发现项加入白名单
有些发现项是故意的(公开的目录表、命名约定、平台行为)。将它们添加到项目的 `allowlist` 中:
```
{
"allowlist": [
{
"surface": "bucket:public-assets",
"reason": "Bucket is intentionally public — landing-page images. No PII."
},
{
"surface": "rpc:show_limit",
"reason": "pg_trgm built-in returning Postgres trigram threshold (0.3). Not user data."
}
]
}
```
被加入白名单的发现项会降级为 `INFO`,并在报告中附注原因 —— 它们是可审计的,而不是被静音。
## 安全性
- 测试用户在每次运行时都会创建带有随机后缀的账号,并在 `finally` 代码块中被删除。
- 每次成功的测试 INSERT 之后,都会跟着执行一次 service-role DELETE;失败的操作则会落入报告的“手动清理”部分。
- JWT 和 service key 在 stderr 日志中会被脱敏(仅显示前 8 个字符)。
- `auth.*` 模式会被跳过 —— 那些是 Supabase 的内部机制,不是你的应用。
- 路径遍历探测会写入一些小文本文件;审计程序会将它们清理掉。如果你希望完全跳过某个存储桶,可以在你的项目配置中将它添加到 `skip_buckets` 中(待办 —— 目前请使用 `--only` 来限制阶段)。
- **仅供授权使用。** 旨在测试你自己的 Supabase 项目。请勿将其指向你不拥有的项目。
## 限制
- 针对 INSERT Payload 的 Schema 推断属于尽力而为;一些具有不寻常的 NOT NULL 约束的表会导致探测返回 400(会被记录为 PASS —— RLS 或检查约束发挥了作用)。
- JWT 重放仅供参考。Supabase 访问令牌在设计上是无状态的,并在过期前一直有效。
- Edge Functions 不会被自动发现(没有公开的列举端点);字典覆盖了大约 50 个常见名称。
- 所有者列的启发式方法会查找 `created_by`、`user_id`、`owner_id`、`owner`、`author_id`。具有非标准所有者列的表无法针对 user_b 泄漏检测进行交叉检查。
- Realtime Payload 泄漏测试最多检查 3 张表并等待 8 秒 —— 如果需要,可以在 `attack_realtime_payload.py` 中增加该值。
- 某些 Management API 检查(PITR、分支)需要 Supabase 个人访问令牌 —— 如果你需要这些检查,请在项目 JSON 中设置 `management_pat`。
- 免费的 Supabase 计划不会暴露所有的 Management API 端点;受影响的检查会输出 `INFO` 并附带“paid plan only”的注释## 发现项的评分方式
| 严重程度 | 含义 |
|---|---|
| CRITICAL | 当前即可从开放互联网进行活跃的数据外泄。立即停止手头工作并优先修复。 |
| HIGH | 具有可衡量影响(特定数据类别、特定角色)的具体利用路径。本周内修复。 |
| MEDIUM | 加固 / 纵深防御。在处理周边相关代码时进行修复。 |
| LOW | 最佳实践、性能或运维卫生。在季度清理时修复。 |
| INFO / ALLOWLISTED | 故意为之或超出你控制范围的平台行为。已记录在案,不是 Bug。 |
## 架构
```
supabase_audit.py # CLI orchestrator + run modes
audit/__init__.py # Finding / TestResult / AuditContext dataclasses
audit/client.py # SupabaseClient (anon, user, service-role)
audit/enumerate.py # Tables / RPCs / buckets discovery via OpenAPI + Storage API
audit/attack_*.py # 20 pentest phases
audit/arch_*.py # 4 architectural phases
audit/sql_static_checks.py # CVE-pattern scans via _audit_static_query helper
audit/report.py # Markdown report writer
projects/_template.json # Project config template
install_static_helper.sql # One-time SECURITY DEFINER helper for static checks
reports/ # Output reports (gitignored)
```
每个 `attack_*.py` / `arch_*.py` 阶段都会导出一个 `run(ctx)` 函数,该函数会修改 `ctx.findings` 和 `ctx.results`。通过在 `supabase_audit.py` 的 `PHASES` 元组中进行添加,即可接入新的阶段。
## 贡献
欢迎提交 PR。要添加新的审计阶段:
1. 创建 `audit/attack_yourthing.py`,导出 `run(ctx: AuditContext) -> None`。
2. 将阶段名称添加到 `supabase_audit.py` 的 `PHASES` 中。
3. 在 `run_audit()` 中添加 `if should("attack_yourthing"):` 块。
4. 在 README 的阶段表格中添加一行。
保持模块小巧 —— 一个文件只关注一个功能,最多约 200 行。复用 `audit.client.anon_client` / `service_client` / `user_client`。优先使用 `ctx.add_finding(...)` + `ctx.add_result(...)` 而不是 print 语句。
## 相关工作
- [Supabase Database Linter](https://supabase.com/docs/guides/database/database-linter) — 内置于控制台的静态检查
- [Supabase Security Advisor](https://supabase.com/dashboard/project/_/database/security-advisor) — 第一方安全建议
- [pgaudit](https://www.pgaudit.org/) — Postgres 审计日志扩展
`supabase-audit` 通过**主动的黑盒探测**和**多用户攻击模拟**对这些工具进行了补充 —— 而不仅仅是静态的规则匹配。
## 许可证
MIT — 详见 [LICENSE](LICENSE)。
## 关键词
supabase security audit · supabase pentest · postgres rls scanner · postgrest security · supabase vulnerability scanner · pg_graphql audit · supabase storage security · realtime rls audit · row-level-security audit · postgrest pentesting · supabase compliance audit · supabase gdpr audit
标签:API安全, CISA项目, DevSecOps, GraphQL, JSON输出, JWT, PostgreSQL, Python, RLS, RPC, Supabase, Web安全, Web报告查看器, 上游代理, 存储安全, 安全合规, 实时通信, 开源安全工具, 无后门, 架构审计, 测试用例, 网络代理, 蓝队分析, 行级安全, 边缘函数, 逆向工具, 逆向工程平台, 黑盒测试