应重点关注Host≠'localhost'且Super_priv='Y'或Grant_priv='Y'的账号,并检查authentication_string为空或过短的弱密码情况。

如何从 mysql.user 表识别高危权限账号
直接查 mysql.user 表能暴露大量权限配置细节,但默认字段(如 Select_priv、Super_priv)是 Y/N 字符串,不能直观看出“谁有 root 级能力”。关键要关注三个维度:是否允许远程登录(Host != 'localhost')、是否拥有管理权限(Super_priv = 'Y' 或 Grant_priv = 'Y')、是否使用空密码或弱密码哈希(authentication_string 为空或长度过短)。
常见错误现象:脚本只检查 password_expired = 'Y',却忽略 plugin = 'mysql_native_password' 且 authentication_string 为空的账号——这类账号可免密登录。
- 用
SELECT User, Host, authentication_string, Super_priv, Grant_priv FROM mysql.user WHERE Host != 'localhost' AND (Super_priv = 'Y' OR Grant_priv = 'Y');快速定位远程高危账号 - 检查空密码时,用
CHAR_LENGTH(authentication_string) 比authentication_string = ''更可靠(兼容旧版本 hash 截断) - MySQL 8.0+ 的
caching_sha2_password插件用户,authentication_string非空也不代表安全,需额外验证是否启用require_secure_transport
用 shell 脚本自动化扫描 user 表并输出基线不合规项
别依赖 Python 或外部工具,纯 bash + mysql 命令就能完成轻量扫描。核心难点是把 SQL 查询结果转成可判断的结构,同时避免密码等敏感字段被意外打印。
使用场景:CI/CD 流水线中嵌入权限检查,或每日巡检脚本的一部分。
- 用
mysql -N -s -e "SQL"获取无格式结果(-N去列名,-s禁用表格对齐) - 逐行读取时用
IFS=$'\t'按制表符分割,避免用户名含空格导致字段错位 - 检测到
Super_priv = Y且Host = '%'时,立即 echo 报警并 exit 1,方便上层流程中断 - 不要在脚本里拼接 SQL 字符串传参,防止注入;所有条件写死在 SQL 中,由 MySQL 服务端解析
为什么 mysqldump 不适合导出 user 表做离线审计
mysqldump mysql user 看似方便,但实际会漏掉关键信息:它默认不导出 authentication_string 字段(因该字段被标记为 BINARY),且生成的 INSERT 语句中 Host/User 值可能被单引号包裹,导致后续 grep 解析失败。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
性能影响:dump 全表再解析比直连查询慢 3–5 倍,尤其当 mysql.user 因插件扩展变大(如 Percona Server 含额外权限字段)时更明显。
- 用
SELECT ... INTO OUTFILE写临时文件更可控,但需确保secure_file_priv允许写入目标路径 - 若必须离线分析,改用
SELECT User, Host, plugin, authentication_string, account_locked FROM mysql.user手动导出为 TSV,再用 awk 处理 - MySQL 8.0.17+ 支持
SHOW GRANTS FOR 'u'@'h'返回结构化结果,比解析 user 表更准确,但无法批量执行
扫描脚本必须避开的四个坑
很多现成脚本在真实环境跑崩,不是逻辑错,而是卡在权限、上下文或版本差异上。
- 连接用户必须有
SELECT权限 onmysql.user,但不能给PROCESS或REPLICATION CLIENT——否则本身就不符合基线 - 脚本中写死
localhost作为 host,而实际 MySQL 配置了bind_address = '*'且跳过 DNS 解析,导致127.0.0.1和localhost被视为不同 Host - MySQL 5.7 默认关闭
sql_mode = ONLY_FULL_GROUP_BY,但某些扫描脚本的 GROUP BY 语句在 8.0 下报错,需显式加SET sql_mode = '' - 用
mysql --defaults-file加载配置时,若文件权限是 644,MySQL 客户端会拒绝读取(要求 ≤600),报错File '/path/.my.cnf' not found却不提示权限问题
真正难的不是写出第一条 SELECT,而是让脚本在 5.7/8.0、Oracle/Percona/MariaDB、容器内外各种环境下稳定返回可判定的结果。建议每次新增一个判断条件,都用 mysql -V 和 SELECT VERSION(), @@version_comment 双校验版本分支。










