单纯靠show processlist或information_schema.tables无法发现表扫描尝试,因其执行快、无长连接、不触发锁;应使用performance schema的events_statements_summary_by_digest表结合digest文本、rows_examined与基线对比来识别异常访问模式。

单纯靠 SHOW PROCESSLIST 或查询 information_schema.TABLES 无法发现“正在尝试扫描敏感表”的行为——这类动作往往不留下长连接、不触发锁、甚至不执行完整 SQL,只靠常规会话监控会漏掉。
为什么不能靠 PROCESSLIST 发现表扫描尝试
攻击者常使用合法账号(如低权限应用账户)发起 SELECT * 或 INFORMATION_SCHEMA 查询,这类语句执行快、TIME 字段几乎为 0,STATE 显示 NULL 或 executing,在 PROCESSLIST 中与正常查询无异。更隐蔽的还会分页查、加 SLEEP() 混淆节奏,根本不会堆积在线程列表里。
真正有效的检测入口是 Performance Schema + statement digest
MySQL 5.6+ 的 performance_schema.events_statements_summary_by_digest 表聚合了所有执行过的 SQL 模板及其频次、延迟、扫描行数等指标,是识别“异常表访问模式”的核心依据。
- 重点关注
SCHEMA_NAME非空且DIGEST_TEXT包含SELECT.*FROM或INFORMATION_SCHEMA的记录 - 过滤出
ROWS_EXAMINED高但ROWS_SENT低的语句(典型全表扫描特征) - 对比历史基线:用
COUNT_STAR判断某张表(如mysql.user、information_schema.COLUMNS)近期被查次数是否突增 - 示例查询(查近1小时对敏感系统表的高频访问):
SELECT DIGEST_TEXT, SCHEMA_NAME, COUNT_STAR, SUM_ROWS_EXAMINED FROM performance_schema.events_statements_summary_by_digest WHERE DIGEST_TEXT LIKE '%INFORMATION_SCHEMA%' OR DIGEST_TEXT REGEXP 'SELECT[[:space:]]+\*.*FROM[[:space:]]+(mysql|performance_schema|information_schema)' AND LAST_SEEN > DATE_SUB(NOW(), INTERVAL 1 HOUR) ORDER BY COUNT_STAR DESC LIMIT 10;
audit_log 插件能捕获原始访问意图,但有代价
MySQL 8.0+ 的 audit_log 插件可记录每条语句的用户、IP、时间戳和完整 SQL 文本,对定位“谁在扫哪张表”最直接,但它默认不开启,且开启后日志体积暴涨、I/O 压力明显。
- 启用前必须确认磁盘空间充足,建议配
audit_log_policy = STATEMENTS(只记语句,不记连接/断开事件) - 关键配置项:
audit_log_include_accounts可缩小范围(如只监控行政账号),audit_log_file必须设为独立路径,避免和 error log 冲突 - JSON 格式日志需用
jq解析,例如提取所有查mysql.user的记录:grep '"query":"SELECT.*mysql.user"' /var/log/mysql/audit.log | jq -r '.user.account,.query,.timestamp'
- 注意:插件无法记录未成功解析的语法错误语句(如拼错表名),这类失败尝试仍要靠错误日志里的
Unknown table提示反推
容易被忽略的边界情况:权限绕过与元数据泄露
即使用户没有 SELECT 权限,只要能连上 MySQL,就可能通过 INFORMATION_SCHEMA 获取表结构、列名甚至部分注释——这是很多扫描工具的起点。而 INFORMATION_SCHEMA 查询本身不报错、不记入 error log,也不受 general_log 控制(除非显式开启)。唯一可靠方式是结合 performance_schema 的 digest 统计 + 定期比对 mysql.tables_priv 和 mysql.columns_priv 中是否存在异常授权记录。











