149 lines
3.5 KiB
Python
149 lines
3.5 KiB
Python
|
|
"""
|
|||
|
|
Supabase 安全知识
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from ..base import KnowledgeDocument, KnowledgeCategory
|
|||
|
|
|
|||
|
|
|
|||
|
|
SUPABASE_SECURITY = KnowledgeDocument(
|
|||
|
|
id="framework_supabase",
|
|||
|
|
title="Supabase Security",
|
|||
|
|
category=KnowledgeCategory.FRAMEWORK,
|
|||
|
|
tags=["supabase", "postgresql", "rls", "auth", "baas"],
|
|||
|
|
content="""
|
|||
|
|
Supabase 是一个开源的Firebase替代品,安全性主要依赖于Row Level Security (RLS)。
|
|||
|
|
|
|||
|
|
## 核心安全机制
|
|||
|
|
1. Row Level Security (RLS)
|
|||
|
|
2. JWT认证
|
|||
|
|
3. PostgreSQL权限系统
|
|||
|
|
|
|||
|
|
## 常见漏洞模式
|
|||
|
|
|
|||
|
|
### RLS未启用
|
|||
|
|
```sql
|
|||
|
|
-- 危险 - 表没有启用RLS
|
|||
|
|
CREATE TABLE posts (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
user_id UUID,
|
|||
|
|
content TEXT
|
|||
|
|
);
|
|||
|
|
-- 任何人都可以访问所有数据!
|
|||
|
|
|
|||
|
|
-- 安全 - 启用RLS
|
|||
|
|
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
|
|||
|
|
|
|||
|
|
CREATE POLICY "Users can only see their own posts"
|
|||
|
|
ON posts FOR SELECT
|
|||
|
|
USING (auth.uid() = user_id);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### RLS策略不完整
|
|||
|
|
```sql
|
|||
|
|
-- 危险 - 只有SELECT策略
|
|||
|
|
CREATE POLICY "select_policy" ON posts FOR SELECT
|
|||
|
|
USING (auth.uid() = user_id);
|
|||
|
|
-- INSERT/UPDATE/DELETE没有策略,可能被绕过
|
|||
|
|
|
|||
|
|
-- 安全 - 完整的CRUD策略
|
|||
|
|
CREATE POLICY "insert_policy" ON posts FOR INSERT
|
|||
|
|
WITH CHECK (auth.uid() = user_id);
|
|||
|
|
|
|||
|
|
CREATE POLICY "update_policy" ON posts FOR UPDATE
|
|||
|
|
USING (auth.uid() = user_id);
|
|||
|
|
|
|||
|
|
CREATE POLICY "delete_policy" ON posts FOR DELETE
|
|||
|
|
USING (auth.uid() = user_id);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 服务端密钥泄露
|
|||
|
|
```javascript
|
|||
|
|
// 危险 - 在前端使用service_role密钥
|
|||
|
|
const supabase = createClient(url, 'service_role_key');
|
|||
|
|
// service_role绕过RLS!
|
|||
|
|
|
|||
|
|
// 安全 - 前端只使用anon key
|
|||
|
|
const supabase = createClient(url, 'anon_key');
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 不安全的函数
|
|||
|
|
```sql
|
|||
|
|
-- 危险 - SECURITY DEFINER函数
|
|||
|
|
CREATE FUNCTION get_all_users()
|
|||
|
|
RETURNS SETOF users
|
|||
|
|
LANGUAGE sql
|
|||
|
|
SECURITY DEFINER -- 以函数所有者权限执行
|
|||
|
|
AS $$
|
|||
|
|
SELECT * FROM users;
|
|||
|
|
$$;
|
|||
|
|
|
|||
|
|
-- 安全 - 使用SECURITY INVOKER或添加检查
|
|||
|
|
CREATE FUNCTION get_user_data(target_user_id UUID)
|
|||
|
|
RETURNS SETOF users
|
|||
|
|
LANGUAGE sql
|
|||
|
|
SECURITY INVOKER
|
|||
|
|
AS $$
|
|||
|
|
SELECT * FROM users WHERE id = target_user_id;
|
|||
|
|
$$;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 存储桶权限
|
|||
|
|
```sql
|
|||
|
|
-- 危险 - 公开存储桶
|
|||
|
|
INSERT INTO storage.buckets (id, name, public)
|
|||
|
|
VALUES ('uploads', 'uploads', true);
|
|||
|
|
-- 任何人都可以访问所有文件
|
|||
|
|
|
|||
|
|
-- 安全 - 私有存储桶 + RLS
|
|||
|
|
INSERT INTO storage.buckets (id, name, public)
|
|||
|
|
VALUES ('uploads', 'uploads', false);
|
|||
|
|
|
|||
|
|
CREATE POLICY "Users can access own files"
|
|||
|
|
ON storage.objects FOR SELECT
|
|||
|
|
USING (auth.uid()::text = (storage.foldername(name))[1]);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### JWT验证绕过
|
|||
|
|
```javascript
|
|||
|
|
// 危险 - 不验证JWT
|
|||
|
|
const { data } = await supabase
|
|||
|
|
.from('posts')
|
|||
|
|
.select('*')
|
|||
|
|
.eq('user_id', userIdFromRequest); // 用户可以伪造
|
|||
|
|
|
|||
|
|
// 安全 - 使用auth.uid()
|
|||
|
|
// RLS策略中使用auth.uid()自动从JWT获取用户ID
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Edge Functions安全
|
|||
|
|
```typescript
|
|||
|
|
// 危险 - 不验证请求来源
|
|||
|
|
Deno.serve(async (req) => {
|
|||
|
|
const { userId } = await req.json();
|
|||
|
|
// 直接使用用户提供的userId
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 安全 - 从JWT获取用户
|
|||
|
|
import { createClient } from '@supabase/supabase-js';
|
|||
|
|
|
|||
|
|
Deno.serve(async (req) => {
|
|||
|
|
const authHeader = req.headers.get('Authorization');
|
|||
|
|
const supabase = createClient(url, anonKey, {
|
|||
|
|
global: { headers: { Authorization: authHeader } }
|
|||
|
|
});
|
|||
|
|
const { data: { user } } = await supabase.auth.getUser();
|
|||
|
|
// 使用验证过的user.id
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 安全检查清单
|
|||
|
|
1. 所有表都启用了RLS
|
|||
|
|
2. 每个表都有完整的CRUD策略
|
|||
|
|
3. 前端只使用anon key
|
|||
|
|
4. service_role key只在服务端使用
|
|||
|
|
5. 存储桶有适当的访问策略
|
|||
|
|
6. 函数使用SECURITY INVOKER
|
|||
|
|
7. Edge Functions验证JWT
|
|||
|
|
""",
|
|||
|
|
)
|