第03章:AI生成的 GRANT ALL PRIVILEGES,权限给得太大了
第03章:AI生成的 GRANT ALL PRIVILEGES,权限给得太大了
“你让 AI 帮你创建数据库用户,它给了你 GRANT ALL PRIVILEGES ON DATABASE。你的 API 服务用的是这个账号——一旦被 SQL 注入,攻击者可以 DROP 整个数据库。”
ℹ️ 版本说明:本章基于 PostgreSQL 18.4,参考最小权限原则(Principle of Least Privilege)。
3.1 AI默认会生成什么
你让 AI 帮你为应用创建数据库用户:
CREATE USER myapp WITH PASSWORD 'secret123';
GRANT ALL PRIVILEGES ON DATABASE mydb TO myapp;
这个账号现在有什么权限?
ALL PRIVILEGES ON DATABASE包含:CONNECT、CREATE、TEMP- 但不包含对表的权限(这是一个常见误解)
- AI 通常接着给你:
GRANT ALL ON ALL TABLES IN SCHEMA public TO myapp——这才真正给了所有表的读写删除权限
现在 myapp 账号可以:读、写、删除所有表、创建表、删除表,但不能 DROP DATABASE(这需要超级用户)。
对于一个只需要读写业务数据的 API 服务来说,这权限太大了。
3.2 AI通常遗漏的4个坑
⚠️ 坑1:API 账号不应该有 DDL 权限
API 服务只需要做 SELECT/INSERT/UPDATE/DELETE(DML),不需要 CREATE/DROP/ALTER(DDL)。
但 GRANT ALL 包含了所有权限,一旦 SQL 注入漏洞被利用:
-- 攻击者注入 SQL,通过 API 账号执行:
DROP TABLE users;
TRUNCATE orders;
CREATE USER attacker SUPERUSER;
最小权限方案:
-- 只给 DML 权限
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO myapp;
-- 不给 CREATE、DROP、ALTER
⚠️ 坑2:没有区分读账号和写账号
对于有只读副本(Read Replica)的架构,读操作应该走只读副本,写操作走主库:
-- 读账号(只给 SELECT)
CREATE USER myapp_readonly WITH PASSWORD '...';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO myapp_readonly;
-- 写账号(给 DML,不给 DDL)
CREATE USER myapp_writer WITH PASSWORD '...';
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO myapp_writer;
-- 迁移账号(给 DDL,仅在迁移时使用)
CREATE USER myapp_migration WITH PASSWORD '...';
GRANT ALL ON SCHEMA public TO myapp_migration;
⚠️ 坑3:密码明文在 SQL 文件里
AI 生成的 SQL 经常包含明文密码:
CREATE USER myapp WITH PASSWORD 'MyPassw0rd!';
这个 SQL 文件可能被提交到 Git 仓库,或者出现在 Bash 历史记录里。
更安全的方式:
# 方式1:通过环境变量传入(不在命令历史中暴露)
psql -c "CREATE USER myapp WITH PASSWORD '$DB_PASSWORD'"
# 方式2:SCRAM-SHA-256 认证(PostgreSQL 14+ 默认)
# 密码在传输和存储时都是加密的
⚠️ 坑4:忘记给序列(Sequences)权限
在 PostgreSQL 里,GRANT ALL ON ALL TABLES 不包含 Sequences(自增 ID 用的序列)。如果用户需要 INSERT,也需要对序列的 USAGE 权限:
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO myapp;
-- 或者只给 USAGE(INSERT 时更新序列)
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO myapp;
如果少了这个,INSERT 操作会报错:
ERROR: permission denied for sequence users_id_seq
3.3 更好的提示词
提示词 P01:为应用服务生成最小权限账号
使用时机:为任何应用服务创建数据库账号
比默认多了什么:
- 区分不同角色的权限
- 不给 DDL 权限
- 包含序列权限
帮我为 PostgreSQL 18.4 应用创建最小权限数据库账号,遵循最小权限原则。
应用架构:
- 主数据库(读写)+ 只读副本
- API 服务(需要读写业务数据)
- 数据分析服务(只读,可以跑复杂查询)
- 数据库迁移工具(需要 DDL 权限,仅在部署时使用)
数据库名:myapp_db
Schema:public
表:users, orders, products, sessions(所有业务表)
帮我创建以下账号(包含完整 SQL):
1. myapp_api:
- 只允许 SELECT、INSERT、UPDATE、DELETE
- 不允许 CREATE、DROP、ALTER
- 需要序列权限(用于自增 ID)
- 连接数限制:最多20个并发连接
2. myapp_readonly:
- 只允许 SELECT
- 无法修改任何数据
3. myapp_migration(仅限迁移时使用):
- 需要 DDL 权限
- 如何在迁移完成后降低权限/删除账号?
同时告诉我:
- 如何不在命令行历史里暴露密码(用环境变量)?
- 如何验证账号的实际权限:\dp tablename 怎么读?
- 新建表或视图后,需要重新 GRANT 吗?(DEFAULT PRIVILEGES 的用法)
基于 PostgreSQL 18.4。
提示词 P02:审计现有账号权限
使用时机:检查现有数据库账号是否权限过大
比默认多了什么:
- 列出所有账号权限
- 识别过大权限
- 收缩权限的 SQL
帮我写审计 PostgreSQL 18.4 数据库账号权限的 SQL 查询。
我想检查:
1. 列出所有用户和角色:
- SELECT * FROM pg_roles 的关键字段解读
- 哪些账号是 SUPERUSER?(这应该尽量少)
2. 列出某个用户对所有表的权限:
- 查询 information_schema.role_table_grants
- 找出有 DELETE 或 TRUNCATE 权限的账号
3. 找出超出最小权限的账号:
- API 账号(应该只有 DML,没有 DDL)
- 是否有账号有 SUPERUSER 权限但不应该有?
4. 权限收缩操作:
- 如何撤销特定权限:REVOKE DELETE ON ALL TABLES FROM myapp
- 撤销权限时有哪些注意事项(是否会影响当前连接)?
给我可以直接运行的 SQL 查询。
基于 PostgreSQL 18.4。
提示词 P03:行级安全(Row Level Security)
使用时机:多租户场景,不同用户只能看到自己的数据
比默认多了什么:
- RLS(Row Level Security)配置
- 防止数据泄露的多租户隔离
帮我在 PostgreSQL 18.4 中实现行级安全(Row Level Security),用于多租户 SaaS 应用。
场景:
- 每个租户有独立的 tenant_id
- 同一个数据库,orders 表存了所有租户的数据
- API 应用账号(myapp_api)不应该能读到其他租户的数据
实现要求:
1. 开启 orders 表的 RLS:
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
2. 创建策略:当前连接的 tenant_id(通过 session 变量传入)只能看到自己的数据
3. 应用层如何传入当前租户 ID:
- 在每次数据库连接后执行 SET app.current_tenant = '123'
- PostgreSQL 如何读取这个变量:current_setting('app.current_tenant')
4. 管理账号绕过 RLS(BYPASS RLS)
5. RLS 的性能影响:
- 每个查询都会加额外的 WHERE 条件,对索引的影响
- 如何验证 RLS 策略在生效(EXPLAIN 里看不到策略,如何调试)
给我完整的 SQL 代码。
基于 PostgreSQL 18.4。
3.4 验收清单
| 检查项 | 验证方法 | AI辅助 |
|---|---|---|
| API 账号无 DDL 权限 | 用 API 账号执行 CREATE TABLE,应该失败 | 用 P01 重建权限 |
| API 账号无 DROP 权限 | 用 API 账号执行 DROP TABLE,应该失败 | 让 AI 生成 REVOKE 语句 |
| 有序列权限 | INSERT 操作不报 sequence permission denied | 让 AI 添加 GRANT USAGE ON SEQUENCES |
| 密码不在 Git 里 | git grep 'PASSWORD' 无结果 |
让 AI 改用环境变量方案 |
| 超级用户最少 | SELECT * FROM pg_roles WHERE rolsuper=true 只有 postgres |
让 AI 检查 superuser 列表 |
| DEFAULT PRIVILEGES 设置 | 新建表自动继承权限 | 让 AI 添加 ALTER DEFAULT PRIVILEGES |
3.5 本章小结
如果你只记一件事:把 API 服务的数据库账号权限从 GRANT ALL 改为 GRANT SELECT, INSERT, UPDATE, DELETE,去掉 DDL 权限。这一个改动让 SQL 注入攻击的最坏结果从"删光所有表"变成"只能改业务数据"。
数据库账号的三个分级:
- 应用账号(DML only):SELECT/INSERT/UPDATE/DELETE,API 服务用这个,不能改表结构
- 只读账号(SELECT only):数据分析、报表服务,不能写任何数据
- 迁移账号(DDL):只在数据库变更时使用,用完锁回或删除
→ 第4章:AI配置了 PostgreSQL 但没有连接池,高并发下连接数爆炸