PCI DSS 要求用戶每 90 天輪換密碼。MySQL 5.6.6+ 引入 password_expired、5.7.4+ 引入 default_password_lifetime 全局策略、5.7.8+ 引入賬戶鎖定 ACCOUNT LOCK/UNLOCK。本文系統梳理密碼過期策略配置、自動化輪換、賬戶生命週期管理、合規審計全流程。
| 版本 | 特性 | 配置方式 |
|---|---|---|
| 5.6.6+ | password_expired 列 (mysql.user) | ALTER USER ... PASSWORD EXPIRE |
| 5.7.4+ | default_password_lifetime 全局變量 | my.cnf / SET GLOBAL |
| 5.7.8+ | 賬戶鎖定 ACCOUNT LOCK/UNLOCK | CREATE/ALTER USER ... ACCOUNT LOCK |
| 8.0+ | 密碼歷史、重用間隔、驗證插件 | password_history, password_reuse_interval |
-- 立即過期 (下次登入強制修改)
ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE;
-- 用戶登入後僅能執行 SET PASSWORD
SET PASSWORD = PASSWORD('NewStrongPass123!');
-- 或 8.0+
ALTER USER USER() IDENTIFIED BY 'NewStrongPass123!';
ERROR 1820 (HY000): You must SET PASSWORD before executing this statement[mysqld] # 90 天過期 (PCI DSS) default_password_lifetime=90 # 永不過期 (內部系統) default_password_lifetime=0
SET GLOBAL default_password_lifetime = 90; SET PERSIST default_password_lifetime = 90; -- 8.0+ 持久化到 mysqld-auto.cnf
-- 用戶專屬 30 天 ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE INTERVAL 30 DAY; -- 用戶永不過期 ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE NEVER; -- 用戶遵循全局策略 ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE DEFAULT;
-- 創建鎖定賬戶 CREATE USER 'furrywall'@'localhost' IDENTIFIED BY 'Pass123!' ACCOUNT LOCK; -- 鎖定現有賬戶 ALTER USER 'furrywall'@'localhost' ACCOUNT LOCK; -- 錯誤: ERROR 3118 (HY000): Access denied for user 'furrywall'@'localhost'. Account is locked. -- 解鎖 ALTER USER 'furrywall'@'localhost' ACCOUNT UNLOCK;
#!/bin/bash
# check_password_expiry.sh
mysql -uroot -p -e "
SELECT User, Host,
password_last_changed,
IF(default_password_lifetime=0, 'NEVER',
DATE_ADD(password_last_changed, INTERVAL default_password_lifetime DAY)
) AS expiry_date
FROM mysql.user
WHERE password_expired = 'N'
AND default_password_lifetime > 0
AND DATE_ADD(password_last_changed, INTERVAL default_password_lifetime DAY) < DATE_ADD(NOW(), INTERVAL 7 DAY);
" | mail -s "MySQL 密碼即將過期預警" dba@company.com
ALTER USER ... PASSWORD EXPIRESET PASSWORD / ALTER USERINSTALL COMPONENT 'file://component_validate_password'; -- 或 5.7: INSTALL PLUGIN validate_password SONAME 'validate_password.so'; 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.policy=STRONG;
-- 密碼合規狀態全景
SELECT
User, Host,
password_last_changed,
password_lifetime,
password_expired,
account_locked,
CASE
WHEN password_lifetime = 0 THEN 'NEVER_EXPIRE'
WHEN password_expired = 'Y' THEN 'EXPIRED'
WHEN DATE_ADD(password_last_changed, INTERVAL password_lifetime DAY) < NOW() THEN 'OVERDUE'
WHEN DATE_ADD(password_last_changed, INTERVAL password_lifetime DAY) < DATE_ADD(NOW(), INTERVAL 30 DAY) THEN 'EXPIRING_SOON'
ELSE 'COMPLIANT'
END AS compliance_status
FROM mysql.user
WHERE User NOT IN ('mysql.sys', 'mysql.session')
ORDER BY compliance_status, User;
default_password_lifetime 不影響現有用戶的 password_last_changedpassword_last_changed 計算過期時間,新用戶按新策略PASSWORD() 函數 8.0 已移除ALTER USER ... IDENTIFIED BY 或 SET PASSWORD = 'plaintext' (8.0 自動哈希)KILL 斷開現有會話