Skip to content

PostgreSQL 最小权限原则

角色和权限体系

1. PostgreSQL权限层次

PostgreSQL的权限体系分为以下几个层次:

  • 全局权限:数据库集群级别的权限(如CREATEDB、CREATEROLE、SUPERUSER等)
  • 数据库级权限:单个数据库级别的权限(如CONNECT、CREATE等)
  • schema级权限:schema级别的权限(如USAGE、CREATE等)
  • 对象级权限:表、视图、函数等对象的权限(如SELECT、INSERT、UPDATE、DELETE等)
  • 列级权限:表中特定列的权限

2. 系统内置角色

PostgreSQL提供了一些内置角色,用于简化权限管理:

sql
-- 查看内置角色
SELECT * FROM pg_roles WHERE rolname LIKE 'pg_%';

-- 常用内置角色
-- pg_read_all_data:读取所有数据的权限
-- pg_write_all_data:写入所有数据的权限
-- pg_monitor:监控相关的权限

最小权限实施步骤

1. 角色设计

根据业务需求设计合理的角色结构,建议采用角色继承的方式:

sql
-- 创建基础角色(按功能划分)
CREATE ROLE app_read_role NOLOGIN;
CREATE ROLE app_write_role NOLOGIN;
CREATE ROLE app_admin_role NOLOGIN;

-- 创建应用用户角色
CREATE ROLE app_user LOGIN PASSWORD 'password';
CREATE ROLE app_admin LOGIN PASSWORD 'password';

-- 角色继承
GRANT app_read_role TO app_user;
GRANT app_write_role TO app_user;
GRANT app_admin_role TO app_admin;
GRANT app_user TO app_admin; -- 管理员继承普通用户权限

2. 数据库权限设置

sql
-- 创建数据库
CREATE DATABASE myapp_db;

-- 授予连接权限
GRANT CONNECT ON DATABASE myapp_db TO app_read_role;
GRANT CONNECT ON DATABASE myapp_db TO app_write_role;
GRANT CONNECT ON DATABASE myapp_db TO app_admin_role;

-- 授予schema使用权限
GRANT USAGE ON SCHEMA public TO app_read_role;
GRANT USAGE ON SCHEMA public TO app_write_role;
GRANT USAGE, CREATE ON SCHEMA public TO app_admin_role;

3. 对象权限设置

sql
-- 切换到目标数据库
\c myapp_db;

-- 创建示例表
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  username VARCHAR(50) UNIQUE NOT NULL,
  email VARCHAR(100) UNIQUE NOT NULL,
  password_hash VARCHAR(255) NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

-- 授予表权限
-- 只读角色:仅SELECT权限
GRANT SELECT ON TABLE users TO app_read_role;

-- 读写角色:SELECT, INSERT, UPDATE, DELETE权限
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE users TO app_write_role;

-- 管理员角色:所有权限
GRANT ALL PRIVILEGES ON TABLE users TO app_admin_role;

-- 授予序列权限(如果有)
GRANT USAGE, SELECT ON SEQUENCE users_id_seq TO app_write_role;
GRANT ALL PRIVILEGES ON SEQUENCE users_id_seq TO app_admin_role;

4. 列级权限控制

对于敏感列,可以进行更细粒度的权限控制:

sql
-- 限制普通用户只能访问非敏感列
REVOKE ALL ON TABLE users FROM app_read_role;
REVOKE ALL ON TABLE users FROM app_write_role;

-- 授予部分列的权限
GRANT SELECT (id, username, email, created_at) ON TABLE users TO app_read_role;
GRANT SELECT (id, username, email, created_at), INSERT (username, email, password_hash), UPDATE (username, email) ON TABLE users TO app_write_role;

5. 函数权限控制

sql
-- 创建示例函数
CREATE FUNCTION get_user_by_email(p_email VARCHAR(100))
RETURNS TABLE(id INT, username VARCHAR(50), email VARCHAR(100))
LANGUAGE plpgsql
AS $$
BEGIN
  RETURN QUERY SELECT u.id, u.username, u.email FROM users u WHERE u.email = p_email;
END;
$$;

-- 授予函数执行权限
GRANT EXECUTE ON FUNCTION get_user_by_email(VARCHAR) TO app_read_role;

应用场景实践

1. Web应用权限设计

场景:典型的Web应用,包含普通用户和管理员

权限设计

sql
-- 创建角色
CREATE ROLE webapp_guest NOLOGIN; -- 游客角色
CREATE ROLE webapp_user NOLOGIN; -- 普通用户角色
CREATE ROLE webapp_admin NOLOGIN; -- 管理员角色

-- 游客角色:仅能访问公开数据
GRANT SELECT ON TABLE public_articles TO webapp_guest;

-- 普通用户角色:继承游客权限,可访问自己的数据
GRANT webapp_guest TO webapp_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE user_profiles TO webapp_user;
GRANT SELECT, INSERT, UPDATE ON TABLE user_orders TO webapp_user;

-- 管理员角色:继承普通用户权限,可管理所有数据
GRANT webapp_user TO webapp_admin;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO webapp_admin;

2. 数据分析权限设计

场景:数据分析团队需要访问生产数据,但不能修改

权限设计

sql
-- 创建数据分析角色
CREATE ROLE analyst_role NOLOGIN;
CREATE ROLE analyst1 LOGIN PASSWORD 'password';
GRANT analyst_role TO analyst1;

-- 授予只读权限
GRANT CONNECT ON DATABASE production_db TO analyst_role;
GRANT USAGE ON SCHEMA public TO analyst_role;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO analyst_role;

-- 设置未来创建的表自动授予权限
ALTER DEFAULT PRIVILEGES FOR ROLE db_owner IN SCHEMA public 
  GRANT SELECT ON TABLES TO analyst_role;

3. 开发人员权限设计

场景:开发人员需要在测试环境有修改权限,但在生产环境只有只读权限

权限设计

sql
-- 测试环境
CREATE ROLE dev_test_role LOGIN PASSWORD 'password';
GRANT ALL PRIVILEGES ON DATABASE test_db TO dev_test_role;
GRANT ALL PRIVILEGES ON SCHEMA public TO dev_test_role;

-- 生产环境
CREATE ROLE dev_prod_role LOGIN PASSWORD 'password';
GRANT CONNECT ON DATABASE prod_db TO dev_prod_role;
GRANT USAGE ON SCHEMA public TO dev_prod_role;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO dev_prod_role;

权限管理最佳实践

1. 定期审计权限

sql
-- 查看用户权限
SELECT 
  grantee,
  privilege_type,
  table_schema,
  table_name
FROM information_schema.role_table_grants
WHERE grantee NOT IN ('postgres', 'pg_signal_backend', 'pg_monitor', 'pg_read_all_data', 'pg_write_all_data');

-- 查看角色继承关系
SELECT 
  r.rolname AS role_name,
  m.rolname AS member_name
FROM pg_auth_members am
JOIN pg_roles r ON am.roleid = r.oid
JOIN pg_roles m ON am.member = m.oid
ORDER BY role_name, member_name;

2. 使用行级安全性(RLS)

行级安全性允许根据用户身份控制对表中行的访问:

sql
-- 创建表并启用RLS
CREATE TABLE documents (
  id SERIAL PRIMARY KEY,
  title VARCHAR(100) NOT NULL,
  content TEXT NOT NULL,
  owner_id INT NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

-- 启用RLS
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;

-- 创建策略:用户只能访问自己的文档
CREATE POLICY document_owner_policy ON documents
  USING (owner_id = current_user_id())
  WITH CHECK (owner_id = current_user_id());

-- 授予权限
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE documents TO app_user;

3. 避免使用超级用户

生产环境中应避免使用超级用户账号,创建专门的管理角色:

sql
-- 创建管理角色
CREATE ROLE db_admin LOGIN PASSWORD 'password';
-- 授予必要的管理权限,但不是超级用户
GRANT CREATEDB, CREATEROLE, REPLICATION TO db_admin;

4. 定期轮换密码

sql
-- 修改用户密码
ALTER USER app_user WITH PASSWORD 'new_secure_password';

5. 限制网络访问

通过pg_hba.conf文件限制允许连接的IP地址:

bash
# 只允许特定IP访问
host myapp_db app_user 192.168.1.0/24 md5
host myapp_db app_admin 10.0.0.0/8 md5

监控和告警

1. 监控权限变更

sql
-- 查看最近的权限变更
SELECT 
  datname,
  usename,
  application_name,
  client_addr,
  query,
  query_start,
  now() - query_start AS duration
FROM pg_stat_activity
WHERE query LIKE '%GRANT%' OR query LIKE '%REVOKE%' OR query LIKE '%CREATE ROLE%' OR query LIKE '%ALTER ROLE%';

2. 设置权限告警

结合数据库审计日志和监控系统,设置以下告警:

  • 新用户创建
  • 权限变更
  • 超级用户登录
  • 异常IP地址连接

常见问题(FAQ)

Q1:如何查看用户当前拥有的权限?

A1:使用以下命令查看:

sql
-- 查看当前用户权限
\dp

-- 查看特定用户的权限
SELECT 
  grantee,
  privilege_type,
  table_schema,
  table_name
FROM information_schema.role_table_grants
WHERE grantee = 'app_user';

Q2:如何撤销用户权限?

A2:使用REVOKE命令:

sql
-- 撤销表权限
REVOKE DELETE ON TABLE users FROM app_write_role;

-- 撤销角色继承
REVOKE app_write_role FROM app_user;

-- 撤销数据库权限
REVOKE CONNECT ON DATABASE myapp_db FROM app_user;

Q3:如何实现跨数据库的权限管理?

A3:PostgreSQL不支持直接的跨数据库权限管理,建议:

  • 在每个数据库中创建相同的角色结构
  • 使用角色继承简化管理
  • 考虑使用外部身份认证系统(如LDAP)

Q4:什么是默认权限?如何设置?

A4:默认权限是指为未来创建的对象自动授予的权限:

sql
-- 设置默认权限
ALTER DEFAULT PRIVILEGES FOR ROLE db_owner IN SCHEMA public
  GRANT SELECT ON TABLES TO app_read_role;
  GRANT INSERT, UPDATE, DELETE ON TABLES TO app_write_role;

Q5:如何处理超级用户权限?

A5:最佳实践:

  • 生产环境尽量避免使用超级用户
  • 创建具有特定管理权限的角色
  • 限制超级用户的网络访问
  • 监控超级用户的活动

Q6:如何实现最小权限原则的自动化管理?

A6:可以通过以下方式自动化:

  • 使用数据库迁移工具(如Flyway、Liquibase)管理权限
  • 编写脚本定期审计和调整权限
  • 使用配置管理工具(如Ansible、Terraform)管理数据库权限
  • 结合CI/CD流程自动应用权限变更

Q7:行级安全性(RLS)有什么优缺点?

A7:

  • 优点:提供更细粒度的权限控制,简化应用层权限逻辑
  • 缺点:可能影响查询性能,需要额外的策略维护
  • 建议:在需要细粒度访问控制的场景使用,结合索引优化性能

Q8:如何备份和恢复权限配置?

A8:

sql
-- 备份角色和权限
pg_dumpall --roles-only > roles.sql

-- 恢复角色和权限
psql -f roles.sql postgres