Ubuntu系统中,用户密码哈希值存储在/etc/shadow文件里,默认的密码轮换周期是99999天(约273年),但这并不意味着你可以永远不换密码。实际生产环境中,安全合规要求通常强制设定为90天或180天轮换一次。要修改这个周期,核心操作就是编辑/etc/login.defs文件中的PASS_MAX_DAYS、PASS_MIN_DAYS、PASS_WARN_AGE三个参数,配合chage命令对具体用户进行精细化管理。下面我把整个机制、配置方法、实操步骤和安全建议一次性讲透。

一、Ubuntu shadow文件的密码哈希机制到底是什么

Ubuntu(以及所有基于Linux的系统)把用户密码的哈希值存在/etc/shadow文件中,这个文件只有root权限才能读取。每一行对应一个用户,字段用冒号分隔,典型格式如下:

username:$6$salt$hashvalue:19000:0:99999:7:::

其中第三个字段是密码哈希($6$表示SHA-512加密),第四个字段是"上次修改密码距1970年1月1日的天数",第五个字段是两次修改之间的最小间隔天数,第六个字段是最大有效天数,第七个字段是过期前的警告天数。第六个字段就是我们常说的"密码轮换周期"的核心控制项。默认99999天,意味着密码几乎永不过期。

二、为什么默认99999天是个安全隐患

从技术角度看,99999天的默认值是为了避免用户频繁被强制改密码导致体验差。但从安全角度,这是一个巨大的风险敞口。如果某个用户的密码在一次数据泄露中被窃取,攻击者拿到的哈希值可以无限期地进行离线暴力破解。尤其是当硬件算力不断提升、彩虹表不断更新的今天,一个弱密码的哈希可能在几小时内就被破解。

更关键的是,很多企业安全审计标准(如等保2.0、ISO 27001、CIS Benchmark)都明确要求密码最长有效期不超过90天。Ubuntu默认不强制轮换,等于把合规责任完全推给了管理员。所以,手动配置合理的轮换周期是每一个运维人员的基本功。

三、修改全局密码轮换周期的具体方法

全局配置文件是/etc/login.defs,用任何文本编辑器打开:

sudo vim /etc/login.defs

找到以下三个关键参数并修改:

PASS_MAX_DAYS   90
PASS_MIN_DAYS   1
PASS_WARN_AGE   7

PASS_MAX_DAYS设为90,表示密码最长90天必须更换;PASS_MIN_DAYS设为1,表示两次改密码之间至少间隔1天(防止用户连续改回旧密码);PASS_WARN_AGE设为7,表示过期前7天开始提醒用户。这三个值是业界比较通用的安全配置。如果你的环境安全等级更高,可以把PASS_MAX_DAYS改成60甚至30。

修改完保存后,新策略对之后新建的用户立即生效。但对已有用户,需要额外用chage命令逐个设置。

四、用chage命令对单个用户设置轮换策略

chage是专门管理用户密码过期策略的工具,比直接改shadow文件安全得多。查看某个用户当前的密码策略:

sudo chage -l username

输出示例:

Last password change                                    : May 15, 2025
Password expires                                        : Aug 13, 2025
Password inactive                                       : never
Account expires                                         : never
Minimum number of days between password change          : 1
Maximum number of days between password change          : 90
Number of days of warning before password expires        : 7

如果要强制某个用户立即改密码并设置90天周期:

sudo chage -M 90 -m 1 -W 7 -d $(date +%Y-%m-%d) username

参数说明:-M是最大天数,-m是最小间隔天数,-W是警告天数,-d是强制上次修改日期设为今天(这样用户下次登录就必须改密码)。如果要批量处理所有用户,可以写个脚本:

for user in $(awk -F: '$3 >= 1000 {print $1}' /etc/passwd); do
    sudo chage -M 90 -m 1 -W 7 "$user"
done

这个脚本会把所有UID大于等于1000的普通用户(即非系统账户)都设置成90天轮换周期。

五、密码哈希算法的选择同样重要

光设轮换周期还不够,哈希算法本身的强度直接决定了密码被破解的难度。Ubuntu默认使用SHA-512($6$),这已经是比较强的算法了。但如果你的系统比较老,可能还在用MD5($1$)或DES(传统crypt),这些都应该淘汰。

检查系统支持的哈希算法:

sudo authconfig --test | grep hashing

如果需要强制使用SHA-512,编辑/etc/pam.d/common-password,找到类似这样的行:

password [success=1 default=ignore] pam_unix.so sha512

确保sha512参数存在。如果你想更进一步,可以考虑引入bcrypt或Argon2,但这需要额外安装libpam-argon2或libpam-bcrypt包,并且要注意兼容性问题。

六、shadow文件权限和审计不可忽视

/etc/shadow文件的权限必须是640或600,属主是root,属组是shadow。如果权限被错误地改成了644甚至更开放,任何普通用户都能读取所有密码哈希,后果不堪设想。定期检查:

ls -l /etc/shadow

正常输出应该是:

-rw-r----- 1 root shadow 1234 Jun 1 10:00 /etc/shadow

另外,建议开启auditd审计,监控shadow文件的访问行为:

sudo auditctl -w /etc/shadow -p rw -k shadow_access

这样任何对shadow文件的读写操作都会被记录到/var/log/audit/audit.log中,方便事后追溯。

七、自动化轮换和PAM集成方案

对于服务器数量多的环境,手动chage显然不现实。可以通过PAM模块pam_pwquality或pam_cracklib来强制密码复杂度,配合crontab定时任务做批量检查。例如,写一个每周执行的脚本:

#!/bin/bash
EXPIRED_USERS=$(chage -l $(awk -F: '$3 >= 1000 {print $1}' /etc/passwd) 2>/dev/null | awk '/Password expires/ && $NF < 0/{print $1}')
if [ -n "$EXPIRED_USERS" ]; then
    echo "以下用户密码已过期: $EXPIRED_USERS" | mail -s "密码过期告警" admin@example.com
fi

把这个脚本放进/etc/cron.weekly/目录,每周自动检查并发送告警邮件。这是中小规模环境性价比最高的方案。

八、特殊场景下的轮换策略调整

不是所有账户都适合90天轮换。比如服务账户(运行数据库、Web服务的专用账户),频繁改密码会导致服务中断。对于这类账户,建议设置更长的周期(如180天或365天),但同时必须配合SSH密钥认证、限制登录来源IP、禁用交互式登录等补偿措施。用chage单独设置:

sudo chage -M 365 -m 30 service_account

另外,root账户的密码策略建议单独管理。很多安全基线要求root密码至少180天更换一次,并且root登录应该尽量禁用,改用sudo提权。检查root的shadow条目:

sudo grep root /etc/shadow

九、总结和实操建议清单

把上面的内容浓缩成一份可执行的操作清单:第一,修改/etc/login.defs中PASS_MAX_DAYS为90、PASS_MIN_DAYS为1、PASS_WARN_AGE为7;第二,用chage -M 90 -m 1 -W 7对所有普通用户批量设置;第三,确认shadow文件权限为640且属组为shadow;第四,开启auditd监控shadow访问;第五,确认哈希算法为SHA-512;第六,对服务账户单独设置更长周期并配合其他安全措施;第七,建立定期审计机制,每月检查一次过期账户和shadow权限。做到这七步,你的Ubuntu系统密码管理就达到了行业主流安全水平。

密码轮换不是一个设置完就不管的事情,它是一个持续运营的安全流程。周期设得太短用户反感、设得太长风险累积,90天是目前安全与可用性之间比较好的平衡点。根据你自己的业务场景和合规要求做微调,但底线是:绝对不要使用默认的99999天。