Skip to content

PostgreSQL 密码策略

密码加密方式

加密算法概述

PostgreSQL支持多种密码加密算法,不同版本默认使用的算法有所不同:

  • md5:从PostgreSQL 7.4开始支持,结合用户名和密码生成MD5哈希值
  • scram-sha-256:从PostgreSQL 10开始支持,14版本起成为默认加密方式,提供更强的安全性

配置默认加密算法

postgresql.conf中配置默认密码加密算法:

ini
# PostgreSQL 10+ 配置
password_encryption = scram-sha-256

# 如果需要兼容旧版本客户端,可以使用以下配置
# password_encryption = md5

版本差异

版本默认加密算法支持的加密算法
9.6及以下md5md5
10-13md5md5, scram-sha-256
14+scram-sha-256md5, scram-sha-256

密码复杂度要求

PostgreSQL本身没有内置的密码复杂度检查机制,但可以通过以下方式实现:

使用check_password_policy扩展

从PostgreSQL 13开始,提供了check_password_policy扩展用于密码复杂度检查:

sql
-- 安装扩展
CREATE EXTENSION IF NOT EXISTS check_password_policy;

-- 配置密码复杂度
ALTER SYSTEM SET password_policy = 'strong';
ALTER SYSTEM SET password_policy_min_length = 12;
ALTER SYSTEM SET password_policy_min_digits = 2;
ALTER SYSTEM SET password_policy_min_uppercase = 2;
ALTER SYSTEM SET password_policy_min_lowercase = 2;
ALTER SYSTEM SET password_policy_min_special = 1;

-- 重载配置
SELECT pg_reload_conf();

使用pg_hba.conf限制

通过pg_hba.conf可以限制连接时的密码验证方式:

# 要求使用scram-sha-256加密
local   all             all                                     scram-sha-256
host    all             all             0.0.0.0/0               scram-sha-256
hostssl all             all             ::/0                    scram-sha-256

使用外部认证机制

结合PAM、LDAP等外部认证机制实现更强的密码复杂度要求:

# 使用PAM进行密码验证
host    all             all             0.0.0.0/0               pam pamservice=postgresql

密码过期策略

使用password_validation扩展

从PostgreSQL 10开始,可以使用password_validation扩展实现密码过期:

sql
-- 安装扩展
CREATE EXTENSION IF NOT EXISTS password_validation;

-- 设置密码有效期为90天
ALTER ROLE username VALID UNTIL '2025-12-31';

-- 检查用户密码有效期
SELECT usename, valuntil FROM pg_user WHERE valuntil IS NOT NULL;

使用自定义函数

创建自定义函数定期检查和提醒密码过期:

sql
CREATE OR REPLACE FUNCTION check_password_expiry()
RETURNS void AS $$
DECLARE
    r RECORD;
BEGIN
    FOR r IN SELECT usename, valuntil FROM pg_user WHERE valuntil IS NOT NULL AND valuntil < CURRENT_DATE + INTERVAL '7 days' LOOP
        RAISE NOTICE 'User % password will expire on %', r.usename, r.valuntil;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

-- 定期执行检查
SELECT check_password_expiry();

密码历史记录

使用pgcrypto扩展

结合pgcrypto扩展实现密码历史记录:

sql
-- 安装扩展
CREATE EXTENSION IF NOT EXISTS pgcrypto;

-- 创建密码历史表
CREATE TABLE password_history (
    username text NOT NULL,
    password_hash text NOT NULL,
    change_date timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
    PRIMARY KEY (username, change_date)
);

-- 创建密码变更触发器
CREATE OR REPLACE FUNCTION record_password_change()
RETURNS trigger AS $$
BEGIN
    INSERT INTO password_history (username, password_hash)
    VALUES (NEW.usename, NEW.passwd);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER password_change_trigger
AFTER UPDATE ON pg_authid
FOR EACH ROW
WHEN (OLD.passwd IS DISTINCT FROM NEW.passwd)
EXECUTE FUNCTION record_password_change();

限制重复使用密码

创建函数检查新密码是否在历史记录中:

sql
CREATE OR REPLACE FUNCTION check_password_reuse(username text, new_password text)
RETURNS boolean AS $$
DECLARE
    history_count integer;
BEGIN
    -- 检查新密码是否与最近3次密码相同
    SELECT COUNT(*) INTO history_count
    FROM password_history
    WHERE username = $1
    ORDER BY change_date DESC
    LIMIT 3;
    
    -- 实现密码匹配逻辑(需要根据实际加密方式调整)
    RETURN history_count < 3;
END;
$$ LANGUAGE plpgsql;

账户锁定机制

使用pg_fail2ban

通过pg_fail2ban扩展实现失败登录尝试的账户锁定:

sql
-- 安装扩展
CREATE EXTENSION IF NOT EXISTS pg_fail2ban;

-- 配置参数
ALTER SYSTEM SET pg_fail2ban.max_tries = 5;
ALTER SYSTEM SET pg_fail2ban.ban_time = 3600; -- 锁定1小时
ALTER SYSTEM SET pg_fail2ban.unban_time = 86400; -- 24小时后自动解锁

-- 重载配置
SELECT pg_reload_conf();

使用pam_tally2

通过PAM的pam_tally2模块实现账户锁定:

bash
# /etc/pam.d/postgresql
auth    required    pam_tally2.so onerr=fail deny=5 unlock_time=3600
account required    pam_tally2.so

最佳实践

生产环境建议

  1. 使用强加密算法:在PostgreSQL 14+中使用默认的scram-sha-256,10-13版本手动配置
  2. 实施密码复杂度要求:结合check_password_policy扩展或外部认证机制
  3. 设置合理的密码有效期:根据业务需求设置90-180天的密码有效期
  4. 维护密码历史记录:限制用户重复使用最近3-5次的密码
  5. 配置账户锁定:对失败登录尝试进行限制,防止暴力破解
  6. 定期审计密码策略:检查用户密码是否符合要求,及时提醒过期密码
  7. 使用角色继承:通过角色继承管理权限,减少直接修改超级用户密码的需求

监控与审计

  • 监控密码过期:定期检查pg_user视图中的valuntil字段
  • 审计密码变更:通过password_history表记录所有密码变更
  • 监控登录失败:通过PostgreSQL日志或pg_fail2ban监控失败登录尝试
  • 定期安全扫描:使用工具如pgAudit或第三方安全扫描工具检查密码策略执行情况

常见问题与解决方案

问题:无法使用旧版本客户端连接

解决方案

  1. 临时将加密算法切换回md5:

    ini
    password_encryption = md5
  2. 为特定用户设置md5加密:

    sql
    ALTER USER username PASSWORD 'md5_hash';
  3. 升级客户端至支持scram-sha-256的版本

问题:忘记超级用户密码

解决方案

  1. 以单用户模式启动PostgreSQL:

    bash
    pg_ctl stop
    postgres --single -D /var/lib/postgresql/data
  2. 重置密码:

    sql
    ALTER USER postgres PASSWORD 'new_password';
  3. 重启PostgreSQL服务:

    bash
    pg_ctl start

问题:密码策略配置不生效

解决方案

  1. 检查扩展是否正确安装:

    sql
    SELECT * FROM pg_extension WHERE extname = 'check_password_policy';
  2. 检查配置参数是否正确设置:

    sql
    SHOW password_policy;
  3. 确保配置已重载:

    sql
    SELECT pg_reload_conf();
  4. 检查日志文件中是否有相关错误信息

总结

PostgreSQL密码策略是数据库安全的重要组成部分,通过合理配置密码加密方式、复杂度要求、过期策略、历史记录和账户锁定机制,可以有效提高数据库的安全性。DBA应根据业务需求和合规要求,选择适合的密码策略方案,并定期进行审计和监控,确保密码策略的有效执行。