3. 默认账户与空口令账户清查
清查对象与判定口径
默认账户与空口令账户清查的核心不是“列出所有用户名”,而是把账户分成三类风险对象并给出可执行处置:默认/内置账户(如 root、postgres、sa、sys、system、dbsnmp、scott 等)、测试/临时账户(命名含 test、demo、tmp、dev、guest 等)、空口令或弱口令账户(口令为空、等于用户名、或仅 1~2 位)。判定口径必须统一,否则不同实例的清单无法横向比较。
本次清查以 MySQL 8.0 为例,同时给出 PostgreSQL 与 SQL Server 的等价查询思路。清查前先明确三个约束:
- 只读采集:查询系统表/视图,不修改任何账户,避免影响业务。
- 口令不可见:数据库不存储明文口令,空口令只能通过“认证尝试”或“口令哈希为空/特殊值”间接判断。
- 处置分级:默认账户优先“禁用或改密”,测试账户优先“确认归属后删除”,空口令账户必须“立即改密或锁定”。
采集:账户清单与口令状态
MySQL 8.0 的账户信息在 mysql.user 中,关键字段为 user、host、plugin、authentication_string、account_locked。空口令的典型特征是 authentication_string 为空字符串且 plugin 为 mysql_native_password 或 caching_sha2_password。以下查询直接输出风险清单:
-- MySQL 8.0:默认账户、测试账户、空口令账户统一清查
SELECT
user,
host,
plugin,
account_locked,
CASE
WHEN authentication_string = '' OR authentication_string IS NULL THEN '空口令'
WHEN user IN ('root','mysql','postgres','sa','sys','system','dbsnmp','scott','admin','test') THEN '默认/内置账户'
WHEN LOWER(user) REGEXP 'test|demo|tmp|dev|guest|temp' THEN '测试/临时账户'
ELSE '普通账户'
END AS risk_type,
CASE
WHEN authentication_string = '' OR authentication_string IS NULL THEN '立即改密或锁定'
WHEN user IN ('root','mysql','postgres','sa','sys','system','dbsnmp','scott','admin','test') THEN '禁用或改密'
WHEN LOWER(user) REGEXP 'test|demo|tmp|dev|guest|temp' THEN '确认归属后删除'
ELSE '纳入常规审计'
END AS action
FROM mysql.user
WHERE user NOT IN ('mysql.sys','mysql.session','mysql.infoschema')
ORDER BY risk_type, user, host;
PostgreSQL 使用 pg_shadow(需超级用户)或 pg_authid,空口令表现为 passwd IS NULL;SQL Server 使用 sys.sql_logins,空口令表现为 password_hash IS NULL 且 is_disabled = 0。三者的共同点是:口令哈希为空 = 空口令,这是最可靠的静态判据。
SELECT * FROM mysql.user 直接导出,避免把 authentication_string 哈希值写入日志或工单。清查脚本应只输出 user、host、risk_type、action 四列。风险清单与处置方案
假设某实例清查结果如下(示例数据,非真实运行结果):
| user | host | plugin | account_locked | risk_type | action |
|---|---|---|---|---|---|
| root | % | caching_sha2_password | N | 默认/内置账户 | 禁用或改密 |
| root | localhost | caching_sha2_password | N | 默认/内置账户 | 禁用或改密 |
| test01 | % | mysql_native_password | N | 测试/临时账户 | 确认归属后删除 |
| dev_app | 10.0.0.% | caching_sha2_password | N | 测试/临时账户 | 确认归属后删除 |
| guest | % | mysql_native_password | N | 空口令 | 立即改密或锁定 |
| backup | 10.0.0.5 | caching_sha2_password | N | 普通账户 | 纳入常规审计 |
处置脚本按风险等级执行,先锁定空口令账户,再处理默认账户,最后清理测试账户:
-- 1) 空口令账户:立即锁定,阻断匿名/空口令登录
ALTER USER 'guest'@'%' ACCOUNT LOCK;
-- 2) 默认账户:限制 root 仅本地登录,并强制改密
ALTER USER 'root'@'%' ACCOUNT LOCK;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'Str0ng#Root_2024!';
ALTER USER 'root'@'localhost' PASSWORD EXPIRE;
-- 3) 测试账户:确认无业务连接后删除
DROP USER 'test01'@'%';
DROP USER 'dev_app'@'10.0.0.%';
-- 4) 复核:再次执行清查查询,确认空口令与默认账户已消除
SELECT user, host, account_locked, authentication_string = '' AS is_empty
FROM mysql.user
WHERE user IN ('root','guest','test01','dev_app');
复核通过条件:guest 的 account_locked = Y;root@% 已锁定;root@localhost 的 authentication_string 非空且 password_expired = Y;test01、dev_app 查询结果为空。
常见误判与规避
- 把
mysql.sys等系统账户当风险:这些是 MySQL 8.0 内部账户,应排除,否则清单噪声过大。 - 只看用户名不看 host:
root@localhost与root@%风险完全不同,前者可保留但需强口令,后者应直接锁定。 - 用
authentication_string = ''判断所有版本:MySQL 5.7 的password字段为空也代表空口令,需按版本调整字段名。 - 改密后忘记
PASSWORD EXPIRE:默认账户改密后应强制下次登录修改,避免口令长期不变。