Skip to content

PostgreSQL 密码策略与管理

密码加密配置

核心加密参数

sql
-- 密码加密方式
ALTER SYSTEM SET password_encryption = 'scram-sha-256';
-- 加密迭代次数(仅适用于scram-sha-256)
ALTER SYSTEM SET scram_iterations = 4096;
-- 验证配置
SHOW password_encryption;
SHOW scram_iterations;

加密方式选择

加密方式安全性兼容性推荐使用
md5不推荐
scram-sha-256PostgreSQL 10+推荐
password极低不推荐

加密配置最佳实践

  • 使用scram-sha-256加密方式,这是PostgreSQL 13+的默认值
  • 适当调整scram_iterations,平衡安全性和性能
  • 避免使用md5和password加密方式
  • 定期检查用户密码的加密方式

密码复杂度策略

密码复杂度要求

PostgreSQL本身不直接提供密码复杂度检查,但可以通过以下方式实现:

  1. 使用pgcrypto扩展:实现密码复杂度验证
  2. 使用外部认证:如LDAP、Kerberos
  3. 使用客户端工具:如pgAdmin提供密码复杂度检查
  4. 使用自定义函数:在CREATE USER或ALTER USER时调用

自定义密码复杂度函数示例

sql
-- 创建密码复杂度检查函数
CREATE OR REPLACE FUNCTION check_password_strength(password TEXT) RETURNS BOOLEAN AS $$
BEGIN
  -- 密码长度至少8位
  IF LENGTH(password) < 8 THEN
    RAISE EXCEPTION '密码长度至少8位';
  END IF;
  
  -- 包含至少一个大写字母
  IF password !~ '[A-Z]' THEN
    RAISE EXCEPTION '密码必须包含至少一个大写字母';
  END IF;
  
  -- 包含至少一个小写字母
  IF password !~ '[a-z]' THEN
    RAISE EXCEPTION '密码必须包含至少一个小写字母';
  END IF;
  
  -- 包含至少一个数字
  IF password !~ '[0-9]' THEN
    RAISE EXCEPTION '密码必须包含至少一个数字';
  END IF;
  
  -- 包含至少一个特殊字符
  IF password !~ '[!@#$%^&*(),.?":{}|<>]' THEN
    RAISE EXCEPTION '密码必须包含至少一个特殊字符';
  END IF;
  
  RETURN TRUE;
END;
$$ LANGUAGE plpgsql;

-- 创建触发器,在创建用户时检查密码
CREATE OR REPLACE FUNCTION check_user_password() RETURNS TRIGGER AS $$
BEGIN
  PERFORM check_password_strength(NEW.password);
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 创建触发器(需要超级用户权限)
-- CREATE TRIGGER trg_check_user_password BEFORE INSERT OR UPDATE ON pg_catalog.pg_authid FOR EACH ROW EXECUTE FUNCTION check_user_password();

密码复杂度最佳实践

  • 密码长度至少8位
  • 包含大小写字母、数字和特殊字符
  • 避免使用常见密码和字典词
  • 避免使用与用户名相关的密码
  • 定期更换密码

密码有效期管理

配置密码有效期

PostgreSQL本身不支持密码有效期,但可以通过以下方式实现:

  1. 使用外部认证系统:如LDAP支持密码有效期
  2. 使用自定义扩展:如pg_tmplug
  3. 使用自定义表和函数:跟踪密码创建和修改时间

自定义密码有效期实现

sql
-- 创建密码有效期表
CREATE TABLE password_expiry (
  username TEXT PRIMARY KEY,
  password_last_changed TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  password_expiry_date TIMESTAMP,
  FOREIGN KEY (username) REFERENCES pg_catalog.pg_authid(rolname)
);

-- 创建函数更新密码有效期
CREATE OR REPLACE FUNCTION update_password_expiry() RETURNS EVENT_TRIGGER AS $$
BEGIN
  IF tg_tag = 'CREATE ROLE' OR tg_tag = 'ALTER ROLE' THEN
    -- 这里可以添加逻辑来更新密码有效期
    -- 例如:当用户密码更改时,更新有效期
  END IF;
END;
$$ LANGUAGE plpgsql;

-- 创建事件触发器
-- CREATE EVENT TRIGGER trg_update_password_expiry ON ddl_command_end WHEN TAG IN ('CREATE ROLE', 'ALTER ROLE') EXECUTE FUNCTION update_password_expiry();

-- 检查密码是否过期的函数
CREATE OR REPLACE FUNCTION is_password_expired(username TEXT) RETURNS BOOLEAN AS $$
DECLARE
  expiry_date TIMESTAMP;
BEGIN
  SELECT password_expiry_date INTO expiry_date FROM password_expiry WHERE username = $1;
  IF expiry_date IS NOT NULL AND expiry_date < CURRENT_TIMESTAMP THEN
    RETURN TRUE;
  END IF;
  RETURN FALSE;
END;
$$ LANGUAGE plpgsql;

密码有效期最佳实践

  • 密码有效期设置为90天
  • 提前30天、7天、1天发送密码过期提醒
  • 允许用户自行重置密码
  • 管理员可以强制重置过期密码
  • 为服务账号设置较长的密码有效期

密码重置方法

超级用户密码重置

sql
-- 方法1:使用psql命令行重置
ALTER USER postgres WITH PASSWORD 'new_password';

-- 方法2:如果无法登录,修改pg_hba.conf文件
-- 将local all all peer改为local all all trust,然后重启或重载配置
-- 登录后重置密码,再改回原来的认证方式

普通用户密码重置

sql
-- 超级用户重置普通用户密码
ALTER USER username WITH PASSWORD 'new_password';

-- 用户自行重置密码(需要知道旧密码)
ALTER USER username WITH PASSWORD 'new_password' VALID UNTIL 'infinity';

忘记密码的处理流程

  1. 确认用户身份
  2. 超级用户重置密码
  3. 强制用户首次登录时修改密码
  4. 记录密码重置操作

密码重置最佳实践

  • 密码重置需要验证用户身份
  • 密码重置后,强制用户首次登录时修改密码
  • 记录所有密码重置操作
  • 使用安全的方式传递新密码
  • 避免在日志中记录明文密码

密码安全监控

监控登录失败

sql
-- 查看登录失败次数
SELECT * FROM pg_stat_auth_members;

-- 查看当前连接
SELECT * FROM pg_stat_activity;

-- 配置登录失败日志
ALTER SYSTEM SET log_connections = on;
ALTER SYSTEM SET log_disconnections = on;
ALTER SYSTEM SET log_failed_connections = on;

监控密码策略合规性

sql
-- 检查用户密码加密方式
SELECT rolname, rolpassword FROM pg_authid WHERE rolpassword IS NOT NULL;

-- 检查用户创建时间
SELECT rolname, rolcreated FROM pg_authid;

-- 检查哪些用户有超级用户权限
SELECT rolname FROM pg_authid WHERE rolsuper = true;

安全监控最佳实践

  • 启用登录失败日志
  • 监控异常登录尝试
  • 定期检查用户权限和密码加密方式
  • 配置告警,当登录失败次数超过阈值时通知
  • 定期审计用户密码策略合规性

密码管理最佳实践

日常管理

  • 定期更换密码,建议90天更换一次
  • 使用强密码策略
  • 避免共享密码
  • 使用密码管理工具存储密码
  • 为不同环境使用不同的密码

服务账号管理

  • 为每个服务创建独立的数据库账号
  • 服务账号密码定期更换
  • 服务账号权限最小化
  • 避免在代码中硬编码密码
  • 使用环境变量或配置文件管理服务账号密码

权限管理

  • 遵循最小权限原则
  • 定期审计用户权限
  • 及时撤销不再需要的权限
  • 使用角色管理权限
  • 限制超级用户数量

常见问题(FAQ)

Q1:如何查看用户的密码加密方式?

A1:查看用户密码加密方式的方法:

sql
SELECT rolname, 
       CASE 
         WHEN rolpassword LIKE 'SCRAM-SHA-256%' THEN 'scram-sha-256'
         WHEN rolpassword LIKE 'md5%' THEN 'md5'
         WHEN rolpassword IS NULL THEN 'no password'
         ELSE 'other'
       END AS encryption_type
FROM pg_authid;

Q2:如何将所有用户的密码加密方式从md5转换为scram-sha-256?

A2:转换密码加密方式的步骤:

  1. 修改postgresql.conf中的password_encryption为scram-sha-256
  2. 重载配置
  3. 要求所有用户重新设置密码
  4. 验证所有用户的密码都已转换

Q3:如何强制用户首次登录时修改密码?

A3:强制首次登录修改密码的方法:

  1. 创建一个标记表,记录用户是否已修改密码
  2. 创建一个登录触发器,检查用户是否需要修改密码
  3. 如果需要修改密码,限制用户只能执行密码修改操作

Q4:如何防止暴力破解密码?

A4:防止暴力破解的方法:

  1. 启用登录失败日志
  2. 配置防火墙规则,限制登录IP
  3. 使用fail2ban等工具,自动封禁多次失败的IP
  4. 使用双因素认证
  5. 限制登录尝试次数

Q5:如何安全地存储服务账号密码?

A5:安全存储服务账号密码的方法:

  1. 使用环境变量
  2. 使用加密的配置文件
  3. 使用密钥管理服务(如AWS KMS、HashiCorp Vault)
  4. 避免在代码中硬编码密码
  5. 定期轮换服务账号密码

Q6:如何审计密码策略合规性?

A6:审计密码策略合规性的方法:

  1. 检查用户密码加密方式
  2. 检查密码复杂度是否符合要求
  3. 检查密码是否过期
  4. 检查用户权限是否符合最小权限原则
  5. 检查是否存在共享账号

Q7:如何配置双因素认证?

A7:配置双因素认证的方法:

  1. 使用外部认证系统,如LDAP结合TOTP
  2. 使用PostgreSQL扩展,如pg_totp
  3. 使用代理服务器,在数据库前面添加双因素认证
  4. 使用云服务提供商的双因素认证

Q8:如何处理密码泄露事件?

A8:处理密码泄露事件的步骤:

  1. 立即重置所有可能泄露的密码
  2. 检查系统日志,确定是否有未授权访问
  3. 加强安全监控
  4. 通知相关人员
  5. 调查泄露原因,防止再次发生

Q9:如何备份和恢复密码策略?

A9:备份和恢复密码策略的方法:

  1. 备份postgresql.conf文件
  2. 备份pg_hba.conf文件
  3. 备份自定义的密码策略函数和表
  4. 记录密码策略文档
  5. 恢复时,确保所有配置一致

Q10:如何为不同用户设置不同的密码策略?

A10:为不同用户设置不同密码策略的方法:

  1. 使用角色管理不同的密码策略
  2. 使用外部认证系统,支持基于用户组的密码策略
  3. 使用自定义函数,根据用户名或角色应用不同的密码策略
  4. 使用不同的数据库实例,配置不同的密码策略