安全基线
用网络、认证、角色、对象权限和行级策略形成多层边界
分离角色
CREATE ROLE app_owner NOLOGIN;
CREATE ROLE app_runtime LOGIN;
CREATE ROLE app_migrator LOGIN NOINHERIT;
CREATE SCHEMA app AUTHORIZATION app_owner;
GRANT app_owner TO app_migrator;
GRANT CONNECT ON DATABASE commerce TO app_runtime, app_migrator;
GRANT USAGE ON SCHEMA app TO app_runtime;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_runtime;
ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_runtime;
ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app
GRANT USAGE, SELECT ON SEQUENCES TO app_runtime;运行时角色不拥有对象,迁移角色只在迁移期间 SET ROLE app_owner,所有者角色不登录。ALTER DEFAULT PRIVILEGES 只影响未来由指定创建者创建的对象,不会回补已有对象。
认证与网络
- 只监听需要的接口,用防火墙/安全组限制来源。
- 远程连接要求 TLS,并验证服务端证书;高敏场景考虑客户端证书。
- 新密码认证使用 SCRAM,逐步淘汰 MD5 配置。
pg_hba.conf按具体网络、database、role 从窄到宽编排;修改后 reload 并测试允许与拒绝两条路径。- 管理入口与应用入口分开,避免向公网暴露数据库端口。
search_path 防护
不要信任可写 schema 中的同名对象解析。撤销 public 的默认创建权,并为安全敏感函数固定路径:
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
CREATE FUNCTION app.current_tenant() RETURNS bigint
LANGUAGE sql
STABLE
SECURITY DEFINER
SET search_path = pg_catalog, app
AS $$ SELECT current_setting('app.tenant_id')::bigint $$;
REVOKE ALL ON FUNCTION app.current_tenant() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION app.current_tenant() TO app_runtime;SECURITY DEFINER 函数以所有者权限运行,必须审计所有参数、对象限定名、search path 和执行权限。
行级安全 RLS
ALTER TABLE app.orders ENABLE ROW LEVEL SECURITY;
ALTER TABLE app.orders FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_orders ON app.orders
USING (tenant_id = current_setting('app.tenant_id')::bigint)
WITH CHECK (tenant_id = current_setting('app.tenant_id')::bigint);RLS 启用后,没有适用策略时默认拒绝。超级用户、BYPASSRLS 角色以及通常的表所有者可绕过;FORCE ROW LEVEL SECURITY 让所有者在普通访问中也受策略约束。仍需普通对象权限。
用真实应用角色测试
管理员测试成功不能证明 RLS 有效。测试允许租户、其他租户、缺失 tenant context、插入与更新,并确认连接池每次借出/归还时正确设置和清除上下文。
Secret 与日志
凭据轮换、短期化并由 secret manager 分发。数据库日志避免记录绑定值和敏感 DDL;审计日志限制访问与保留期。pg_stat_activity 也可能显示 SQL 文本,读取监控视图的权限同样需要控制。
Last updated on