你的服务器密码够安全吗?一个脚本快速检测Linux密码策略合规性
每次服务器被攻破后,安全报告里总会出现那句熟悉的结论:"弱密码是导致此次入侵的主要原因"。作为运维工程师,我见过太多因为密码策略松懈引发的安全事故——从数据库被拖库到整个内网沦陷。但手动检查每台服务器的密码复杂度配置既枯燥又容易遗漏关键项。这就是为什么我们需要自动化工具来帮我们完成这项基础但至关重要的工作。
1. 为什么密码策略合规性检查如此重要
去年某科技公司遭遇的供应链攻击事件中,攻击者正是通过一台测试服务器的弱密码(admin/123456)横向渗透到核心业务系统。事后审计发现,该服务器从未启用任何密码复杂度要求。这不是个案——Verizon《2023年数据泄露调查报告》显示,80%的与凭证相关的入侵都利用了弱密码或默认密码。
在Linux系统中,密码策略主要通过PAM(可插拔认证模块)实现,具体配置通常存放在:
- RedHat/CentOS系:
/etc/pam.d/system-auth - Debian/Ubuntu系:
/etc/pam.d/common-password
关键配置参数包括:
| 参数 | 推荐值 | 安全作用 |
|---|---|---|
| minlen | ≥12 | 最小密码长度 |
| lcredit | ≤-1 | 至少1个小写字母 |
| ucredit | ≤-1 | 至少1个大写字母 |
| dcredit | ≤-1 | 至少1个数字 |
| ocredit | ≤-1 | 至少1个特殊字符 |
| retry | ≤3 | 密码尝试次数限制 |
| enforce_for_root | 必须启用 | 对root账户同样生效 |
提示:金融、医疗等行业通常需要满足等保2.0三级要求,其中明确规定密码长度不得少于10位且必须包含三种以上字符类型。
2. 自动化检测脚本设计与实现
下面这个Bash脚本可以自动提取当前系统的密码策略配置,并与安全基线进行对比:
#!/bin/bash # 定义安全基线标准 declare -A BASELINE=( ["minlen"]=12 ["lcredit"]=-1 ["ucredit"]=-1 ["dcredit"]=-1 ["ocredit"]=-1 ) # 检测配置文件路径 if [ -f "/etc/pam.d/system-auth" ]; then CONFIG_FILE="/etc/pam.d/system-auth" elif [ -f "/etc/pam.d/common-password" ]; then CONFIG_FILE="/etc/pam.d/common-password" else echo "未找到密码策略配置文件" exit 1 fi # 提取当前配置 extract_param() { grep -oP "pam_pwquality.*\b$1=\K[-\d]+" $CONFIG_FILE || echo 0 } echo "===== 密码策略合规性检测报告 =====" echo "检测时间: $(date)" echo "配置文件: $CONFIG_FILE" echo "----------------------------------" # 检查各项参数 for param in "${!BASELINE[@]}"; do current=$(extract_param $param) required=${BASELINE[$param]} if [ "$current" -eq "$required" ]; then status="\033[32m符合\033[0m" else status="\033[31m不符合\033[0m" fi printf "%-8s 当前值:%-4s 要求值:%-4s 状态: %b\n" \ "$param" "$current" "$required" "$status" done # 检查enforce_for_root if grep -q "enforce_for_root" $CONFIG_FILE; then echo -e "root强制策略: \033[32m已启用\033[0m" else echo -e "root强制策略: \033[31m未启用\033[0m" fi脚本输出示例:
===== 密码策略合规性检测报告 ===== 检测时间: 2023年8月15日 14:30:22 配置文件: /etc/pam.d/system-auth ---------------------------------- minlen 当前值:8 要求值:12 状态: 不符合 lcredit 当前值:0 要求值:-1 状态: 不符合 ucredit 当前值:-1 要求值:-1 状态: 符合 dcredit 当前值:-1 要求值:-1 状态: 符合 ocredit 当前值:0 要求值:-1 状态: 不符合 root强制策略: 未启用3. 高级检测功能扩展
基础检测只能告诉我们配置是否存在问题,而实际环境中我们还需要:
3.1 密码过期策略检查
在/etc/login.defs文件中添加以下检查项:
# 检查密码过期策略 echo "----- 密码过期策略 -----" grep -E "^PASS_MAX_DAYS|^PASS_MIN_DAYS|^PASS_WARN_AGE" /etc/login.defs # 推荐值参考 cat <<EOF > 安全建议值: > PASS_MAX_DAYS 90 # 密码最长使用期限 > PASS_MIN_DAYS 7 # 密码最短使用期限 > PASS_WARN_AGE 14 # 密码过期前警告天数 EOF3.2 历史密码检查
在/etc/pam.d/system-auth中检查是否配置密码历史记录:
if grep -q "remember" $CONFIG_FILE; then remember=$(grep -oP "remember=\K\d+" $CONFIG_FILE) echo -e "密码历史记录: \033[32m已启用(最近${remember}次)\033[0m" else echo -e "密码历史记录: \033[31m未启用\033[0m" fi3.3 生成HTML报告
对于多服务器环境,可以修改脚本输出HTML格式:
generate_html() { cat <<EOF <html> <head><title>密码策略合规报告</title></head> <body> <h2>$(hostname) - 密码策略报告</h2> <table border="1"> <tr><th>参数</th><th>当前值</th><th>要求值</th><th>状态</th></tr> $(for param in "${!BASELINE[@]}"; do current=$(extract_param $param) required=${BASELINE[$param]} if [ "$current" -eq "$required" ]; then status="✔" else status="✘" fi echo "<tr><td>$param</td><td>$current</td><td>$required</td><td>$status</td></tr>" done) </table> <p>生成时间: $(date)</p> </body> </html> EOF }4. 企业级部署方案
对于拥有上百台服务器的环境,建议采用以下架构:
集中式检测:
- 使用Ansible批量执行检测脚本
- 示例Playbook:
- hosts: all_servers tasks: - name: 上传检测脚本 copy: src: audit_password_policy.sh dest: /tmp/ mode: 0755 - name: 执行检测 command: /tmp/audit_password_policy.sh register: result - name: 收集结果 copy: content: "{{ result.stdout }}" dest: "/var/audit/{{ inventory_hostname }}.txt"
结果可视化:
- 使用ELK堆栈收集所有报告
- 在Kibana中创建合规性仪表盘
自动修复:
- 对于不符合项,自动应用修复脚本:
# 示例修复命令 sed -i 's/minlen=8/minlen=12/g' /etc/pam.d/system-auth sed -i 's/$/ enforce_for_root/' /etc/pam.d/system-auth
- 对于不符合项,自动应用修复脚本:
注意:生产环境中执行自动修复前,务必先进行配置备份和变更评审。
5. 密码策略的最佳实践
根据NIST最新指南和实战经验,建议采用以下策略组合:
- 长度优先:12位以上密码比复杂但简短的密码更安全
- 避免强制轮换:除非怀疑密码泄露,否则频繁更换会导致用户使用可预测模式
- 禁用常见弱密码:
# 在/etc/pam.d/system-auth中添加 password requisite pam_pwquality.so dictcheck=1 - 多因素认证:即使密码策略再强,也应结合SSH密钥或OTP
实际部署时经常会遇到的两个坑:
历史系统兼容性问题:
# 旧版RHEL6需要使用pam_cracklib替代 password requisite pam_cracklib.so try_first_pass retry=3 \ minlen=12 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1特殊字符冲突:
# 某些系统需要排除特定字符 password requisite pam_pwquality.so \ minlen=12 difok=3 reject_username \ noauthtok_check enforce_for_root
在最近一次为客户做的安全加固中,我们发现虽然密码策略配置正确,但由于没有设置enforce_for_root参数,攻击者仍然可以通过root账户设置简单密码。这个小细节让我们意识到自动化检测必须覆盖所有关键参数。