数据库安全基线检查里,密码复杂度和过期天数的设定从来不是“越严格越好”。很多运维人员一上来就配置密码长度20位、大小写加特殊字符全上、30天必须更换,结果第二天业务部门就堵在机房门口,因为数据库连接池全崩了。真正有效的做法是:根据数据库的类型、业务场景和合规要求,找到一个既满足安全基线又不影响业务连续性的平衡点。下面直接讲具体怎么设定。

密码复杂度设定的四个核心维度

密码复杂度不是简单勾选“必须包含特殊字符”就完事,它至少包含四个需要分别配置的维度:最小长度、字符类型组合、历史密码记忆、以及禁用常见弱口令字典。先说最小长度,目前主流数据库安全基线普遍要求至少8位,但这个标准正在被快速淘汰。对于MySQL 8.0和PostgreSQL 15以上的版本,建议直接拉到12位起步,因为8位纯字母密码在普通GPU上暴力破解只需要几分钟。如果你的数据库跑在金融或政务系统里,14位是底线。

字符类型组合这块,很多基线检查工具默认要求“大写字母+小写字母+数字+特殊字符”四选三,这个配置本身没问题,但容易踩的坑是特殊字符集的范围。Oracle数据库里,密码中的“@”和“”符号在某些连接字符串里会被误解析,导致应用连接失败。所以特殊字符集建议限定在“!%^&*_-+=:.”这几个经过验证不会引起连接问题的符号,而不是全量放开。

历史密码记忆这个参数经常被忽略,但它直接决定了密码复杂度策略是否形同虚设。如果用户每次改密码都在“Abc123!”和“Abc1234!”之间来回切换,复杂度再高也没用。MySQL的password_history参数和PostgreSQL的password_reuse_limit都能控制新密码不能与最近几次旧密码重复,建议设置为5次以上。SQL Server则通过CHECK_POLICY选项调用Windows本地安全策略来实现,这个值在Windows默认基线里通常是24次,但实际生产环境设10次就够用了。

最关键也最容易被漏掉的是弱口令字典过滤。等保2.0测评里明确要求数据库不能使用常见弱口令,但大部分基线检查脚本只检查了复杂度规则,没检查密码内容本身。你需要在数据库侧部署一个密码验证插件,比如MySQL的validate_password组件配合自定义字典文件,把“password123”“admin2024”“qwerty!@#”这类组合直接拉黑。这个字典文件需要定期更新,因为攻击者的字典库也在进化。

各主流数据库的复杂度配置方法

MySQL 8.0开始,密码策略完全由validate_password组件控制。安装组件后,几个关键参数需要精确设置:validate_password.length控制最小长度,validate_password.mixed_case_count控制大小写字母最少数量,validate_password.number_count控制数字最少数量,validate_password.special_char_count控制特殊字符最少数量。还有一个容易被误解的参数是validate_password.dictionary_file,它指向的就是弱口令字典文件的绝对路径,文件里每行一个禁用密码,注意文件权限必须设为mysql用户只读。

-- 安装密码验证组件
INSTALL COMPONENT 'file://component_validate_password';

-- 设置密码策略参数
SET GLOBAL validate_password.length = 12;
SET GLOBAL validate_password.mixed_case_count = 1;
SET GLOBAL validate_password.number_count = 1;
SET GLOBAL validate_password.special_char_count = 1;
SET GLOBAL validate_password.dictionary_file = '/etc/mysql/password_dict.txt';
SET GLOBAL validate_password.policy = STRONG;

PostgreSQL的密码复杂度通过passwordcheck模块实现,但这个模块默认只检查用户名是否包含在密码中,功能比较弱。你需要修改源码或者使用增强版插件来支持正则表达式规则。更实际的做法是在应用层或者通过自定义函数在创建用户时做校验,利用PostgreSQL的CHECK约束和事件触发器来拦截不符合复杂度要求的密码。不过这种方案对DBA的技术要求较高,中小团队建议直接用pgAudit记录所有密码变更操作,配合外部合规扫描工具来弥补。

Oracle的密码复杂度由utlpwdmg.sql脚本定义,这个脚本在$ORACLE_HOME/rdbms/admin目录下。默认脚本只做了基础的长度和字符检查,你需要修改其中的FUNCTION verify_function来加入自己的规则,比如禁止包含用户名、禁止连续重复字符超过3个、强制至少包含2个特殊字符等。修改后重新执行脚本即可生效。SQL Server则相对简单,通过Windows本地安全策略或者直接在创建登录名时指定CHECK_POLICY=ON就能启用复杂度要求,但要注意这个选项依赖Windows策略,如果SQL Server跑在Linux容器里,这个选项会失效。

密码过期天数设定的行业实践

密码过期天数这个参数,不同行业和不同数据库角色的设定差异巨大。先说结论:对于应用程序使用的数据库账号,90天强制过期是行业通用基线,但需要配合自动轮转机制;对于人工运维账号,60天更合适;对于只读查询账号和监控账号,可以放宽到180天甚至更长,因为这些账号通常权限受限且暴露面小。

这里有一个很多人不知道的关键点:密码过期策略必须和“密码过期后的处理方式”联动配置。MySQL的default_password_lifetime参数设成90天后,到期账号默认会被锁定,应用直接报错中断。所以你必须同时配置过期宽限期和告警机制。PostgreSQL提供了更精细的控制,通过设置rolvaliduntil参数可以精确到时间戳,而且过期后用户仍然可以用现有连接继续操作,只是不能建立新连接,这个设计对业务更友好。

-- MySQL设置全局密码过期策略为90天
SET GLOBAL default_password_lifetime = 90;

-- 对特定账号设置不同的过期策略
ALTER USER 'app_user'@'%' PASSWORD EXPIRE INTERVAL 180 DAY;
ALTER USER 'dba_user'@'%' PASSWORD EXPIRE INTERVAL 60 DAY;

-- PostgreSQL设置账号过期时间
ALTER ROLE app_user VALID UNTIL '2025-12-31 23:59:59';

Oracle的密码过期通过profile来控制,DEFAULT profile里的PASSWORD_LIFE_TIME默认是180天,但等保要求通常是90天。修改时注意不要直接改DEFAULT profile,而是创建新的profile分配给不同用户组。SQL Server的密码过期完全依赖Windows策略或Azure AD策略,本地SQL账号可以通过ALTER LOGIN语句的CHECK_EXPIRATION选项控制,但这个选项同样在Linux容器环境下不可用。

真正让运维头疼的不是设多少天,而是密码到期前怎么通知用户。数据库本身没有这个通知功能,你需要在外部搭建一套巡检脚本,每天检查所有账号的密码到期时间,对即将在14天内到期的账号发送告警邮件或企业微信通知。脚本逻辑很简单,查询MySQL的mysql.user表的password_last_changed字段,或者PostgreSQL的pg_authid表的rolvaliduntil字段,计算剩余天数后触发告警。这个脚本建议直接集成到监控系统里,而不是单独跑cron。

应用程序账号的特殊处理

应用程序连接数据库用的账号,密码复杂度和过期策略需要单独设计。这类账号的密码通常写在配置文件或配置中心里,人工修改成本高且容易遗漏。强制90天过期如果没有配套的自动轮转方案,结果就是每隔三个月全站故障一次。正确的做法是:密码复杂度拉到最高,长度16位以上,随机生成,全部字符类型都包含;但过期天数可以设成0或者365天,前提是密码存储和传输过程全程加密,并且有异常登录检测机制。

更好的方案是引入动态凭据系统,比如HashiCorp Vault或者云厂商的密钥管理服务,让应用程序每次启动时动态获取数据库密码,用完后自动回收。这种模式下,密码可以设成每小时轮转一次,复杂度反而不用特别高,因为密码生命周期极短,即使泄露也很快失效。如果暂时没有条件上动态凭据,至少要做到密码配置文件和数据库备份文件分离存储,并且配置文件里的密码必须加密,不能用明文。

等保2.0和行业合规的具体要求

等保2.0对数据库密码策略有明确条款,二级系统要求密码长度不少于8位、包含两种以上字符类型、定期更换;三级系统要求密码长度不少于10位、包含三种以上字符类型、更换周期不超过90天。但等保测评时,测评机构不仅看你的配置参数,还会实际尝试创建弱口令账号来验证策略是否真正生效。所以你的基线检查脚本不能只查参数,还要做渗透测试式的验证。

金融行业有更严格的要求,银保监会发布的《商业银行信息科技风险管理指引》要求核心系统数据库密码至少14位,更换周期不超过60天,而且每次更换必须是完全不同的密码,不能只改一个字符。政务云环境通常参照等保三级加政务云安全基线,要求密码过期后账号立即锁定,需要走工单审批流程才能解锁,这对自动化运维提出了更高要求。

这里有一个容易被忽视的合规细节:数据库系统表空间里的默认账号和示例账号必须全部删除或锁定。MySQL安装后的test数据库和匿名用户、Oracle的SCOTT和HR示例账号、PostgreSQL的postgres超级用户如果允许远程登录,都会在基线检查中被判定为高风险项。这些账号的处理优先级甚至高于密码复杂度配置,因为它们是已知的攻击入口。

基线检查自动化脚本的关键检查项

手动检查几十台数据库的密码策略是不现实的,必须靠自动化脚本。一个合格的数据库安全基线检查脚本,针对密码部分至少要覆盖以下检查项:密码复杂度参数是否生效、是否有账号未设置密码、是否有账号使用默认密码、密码过期策略是否配置、过期宽限期是否合理、历史密码保留数量是否足够、弱口令字典是否加载且文件存在、应用程序账号是否有异常的长过期时间、以及是否存在密码明文存储的情况。

脚本实现上,MySQL可以通过查询performance_schema和mysql系统表来获取大部分信息,PostgreSQL查询pg_catalog,Oracle查询dba_users和dba_profiles,SQL Server查询sys.sql_logins和sys.server_principals。输出结果建议统一格式化成JSON,方便对接SIEM或SOC平台。脚本本身不要用数据库超级账号运行,应该创建一个只读权限的巡检专用账号,避免脚本本身成为安全隐患。

最后强调一个观点:密码复杂度和过期天数只是数据库安全基线的一小部分,但它是攻击者最常尝试突破的入口。很多数据泄露事件的溯源结果都指向一个长期未更换的弱口令账号。把这两个参数配置好,配合多因素认证和异常行为检测,才能构成完整的数据库访问安全防线。基线检查的目的不是为了应付测评,而是确保每一条配置都真正在保护你的数据。