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 四列。

风险清单与处置方案

假设某实例清查结果如下(示例数据,非真实运行结果):

userhostpluginaccount_lockedrisk_typeaction
root%caching_sha2_passwordN默认/内置账户禁用或改密
rootlocalhostcaching_sha2_passwordN默认/内置账户禁用或改密
test01%mysql_native_passwordN测试/临时账户确认归属后删除
dev_app10.0.0.%caching_sha2_passwordN测试/临时账户确认归属后删除
guest%mysql_native_passwordN空口令立即改密或锁定
backup10.0.0.5caching_sha2_passwordN普通账户纳入常规审计

处置脚本按风险等级执行,先锁定空口令账户,再处理默认账户,最后清理测试账户:

-- 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.user pg_authid / pg_shadow sys.sql_logins 只读查询,不导出哈希 判定规则 哈希为空 → 空口令 内置名 → 默认账户 test/dev → 测试账户 排除 mysql.sys / mysql.session / mysql.infoschema 处置动作 空口令 → ACCOUNT LOCK 默认 → 禁用或改密 测试 → 确认后 DROP 按风险等级顺序执行 风险清单(示例数据) user host risk_type action 状态 guest % 空口令 立即改密或锁定 待处置 root % 默认/内置账户 禁用或改密 待处置 test01 % 测试/临时账户 确认归属后删除 待确认 backup 10.0.0.5 普通账户 纳入常规审计 正常 复核闭环 再次执行清查查询 → 空口令账户 account_locked = Y → 默认账户已锁定或已改密 → 测试账户查询结果为空

常见误判与规避

  • 把 mysql.sys 等系统账户当风险:这些是 MySQL 8.0 内部账户,应排除,否则清单噪声过大。
  • 只看用户名不看 host:root@localhost 与 root@% 风险完全不同,前者可保留但需强口令,后者应直接锁定。
  • 用 authentication_string = '' 判断所有版本:MySQL 5.7 的 password 字段为空也代表空口令,需按版本调整字段名。
  • 改密后忘记 PASSWORD EXPIRE:默认账户改密后应强制下次登录修改,避免口令长期不变。
清查脚本建议固化为只读账号可执行的视图或存储过程,每月自动运行一次,输出 CSV 清单并对比上月结果,新增的默认/测试/空口令账户即为重点核查对象。