MySQL网络抓包审计
写在前面:抓包审计是排查手段,不是审计方案。要长期留存审计记录,用 MySQL 企业版审计插件、MariaDB Audit Plugin 或 Percona 的 audit_log;抓包只适合「现在就要看到这台机器上正在跑什么 SQL、且不方便改配置重启」的临场排查。
另外三条前提,动手前先确认:
- 抓包会拿到明文 SQL 和其中的业务数据(手机号、身份证、订单号都在里面)。这是敏感操作,要有授权,抓下来的文件也要按敏感数据处置。
- 连接开了 TLS 就抓不到明文。MySQL 8.0 默认对客户端启用 TLS,能抓到明文往往意味着这条链路根本没加密——那本身是个应当上报的问题。
- 高 QPS 实例上抓包会占用可观的 CPU 和磁盘,
-w写文件时更要控制时长和大小,别把磁盘打满。
版本说明
本文写于 2022-03。tcpdump/MySQL 协议抓包分析思路未变,经 2026-07 复核仍适用。
# 方式一:tcpdump + 文本还原(零依赖,适合临场)
网卡名(bond0)和端口(3366)要换成你自己的,先用 ip -br link 和 netstat -lntp | grep mysqld 确认:
#!/bin/bash
# 抓 MySQL 入向流量并还原出 SQL 文本
# -i bond0 抓哪块网卡,按实际改
# -s 0 抓完整包,不截断(漏了它长 SQL 会被切断)
# -l 行缓冲,配合管道才能实时输出
# -w - 写到标准输出交给后面的管道
# dst port 只抓发往 MySQL 的方向,即客户端发来的语句
tcpdump -i bond0 -s 0 -l -w - dst port 3366 | strings | perl -e '
while(<>) { chomp; next if /^[^ ]+[ ]*$/;
if(/^(SELECT|UPDATE|DELETE|INSERT|SET|COMMIT|ROLLBACK|CREATE|DROP|ALTER|CALL)/i)
{
if (defined $q) { print "$q\n"; }
$q=$_;
} else {
$_ =~ s/^[ \t]+//; $q.=" $_";
}
}'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
这个 perl 片段做的事是:把 strings 抽出的文本行按 SQL 关键字开头合并成完整语句(一条 SQL 可能被拆在多个 TCP 包里)。它是尽力还原,不解析 MySQL 协议,所以预处理语句(prepared statement)、压缩协议、TLS 加密的流量它都还原不出来。
想留存下来事后分析,把 -w - 改成写文件、控制好大小:
tcpdump -i bond0 -s 0 -w /tmp/mysql.pcap -C 100 -W 5 dst port 3366
# -C 100 每个文件 100MB,-W 5 最多滚动 5 个,合计上限 500MB,不会撑爆磁盘
2
验证:另开一个终端连上去跑一条 SELECT 1;,抓包侧应当立刻打印出这条语句。抓不到先查三件事:网卡名对不对、端口对不对(很多实例不是 3306)、连接是不是走了 TLS 或 unix socket(走 socket 的本地连接根本不经过网卡,抓不到,这是最常见的"抓不到"原因)。
# 方式二:专用 sniffer 工具(能解析协议,输出更规整)
有两个名字相近但完全不同的工具,网上的教程经常把它们混在一起写,导致「按 A 的步骤编译,却对着 B 的参数敲命令」:
| 工具 | 来源 | 获取方式 |
|---|---|---|
| mysql-sniffer | 奇虎 360 开源,C 写的 | 源码编译,产物是 mysql-sniffer |
| vc-mysql-sniffer | VividCortex(现 SolarWinds)出的闭源二进制 | 官网下载现成二进制,不需要编译 |
下面两节分别对应这两个,别混着用。
# 2.1 mysql-sniffer(360,需编译)
yum install cmake
yum install libpcap-devel
yum install glib2-devel
yum install libnet-devel
2
3
4
git clone https://github.com/Qihoo360/mysql-sniffer.git
cd mysql-sniffer
mkdir proj && cd proj
cmake ../
make
# 产物在 bin/ 目录下:mysql-sniffer
2
3
4
5
6
用法(注意它的参数和下面 vc- 那个完全不一样):
./bin/mysql-sniffer -i bond0 -p 3366 # -i 网卡 -p 端口
# 2.2 vc-mysql-sniffer(VividCortex,下载即用)
这个是预编译二进制,不用上面那套 cmake/make 步骤。它按 /proc/net/tcp 里的 bind 地址来定位要监听的端口:
$ sudo ./vc-mysql-sniffer --binding="[::]:3366" --output=/tmp/queries.log
$ sudo ./vc-mysql-sniffer --help
vc-mysql-sniffer is a utility from VividCortex to monitor query activity and write results to a file.
See --license for the terms governing your usage of this program.
-binding="[::]:3306" This is a list of comma separated bind strings as seen in /proc/net/tcp
-help="false" Show this usage message
-help-json="false" Show this usage message as JSON
-license="false" Print the usage terms of this program
-output="" Filepath to output queries to. Defaults to stdout if none specified.
-show-database="false" Include a 'USE `database`' for every statement. Supersedes show-database-changes.
-show-database-changes="true"
Include a 'USE `database`' every time the database is changed.
-verbose="false" Enable logging on program startup to stderr
-version="false" Show version and exit
Flag Current value
--------------------------------------------
-binding "[::]:3306"
-help "true"
-help-json "false"
-license "false"
-output ""
-show-database "false"
-show-database-changes "true"
-verbose "false"
-version "false"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 坑与边界
- 本地连接抓不到。客户端用
-h localhost时走的是 unix socket,不经过网络栈,tcpdump 抓不到任何东西。要抓本地连接必须让客户端用-h 127.0.0.1(走 TCP 回环),并把-i改成lo。 - TLS 加密的连接只能抓到密文。MySQL 8.0 默认对客户端连接启用 TLS,抓包看到乱码是正常的,不是工具坏了。判断方法:
SELECT * FROM performance_schema.session_ssl_status;看会话有没有加密。 - 预处理语句抓到的是占位符。用
?参数化的语句(绝大多数应用框架的默认行为),抓包看到的 SQL 里参数是分开传的二进制值,文本还原的方式拼不回完整语句。这种场景应该用performance_schema.events_statements_summary_by_digest或慢日志,而不是抓包。 - 抓包不是零开销。高 QPS 下 tcpdump 本身会吃掉一个核,
-s 0抓全包写盘更是 I/O 大户。抓完立刻停,别留着跑。 - 要看「哪些 SQL 慢/频繁」,抓包不是最优解。慢查询日志临时把
long_query_time设成 0 记录全量语句,再用pt-query-digest聚合,比抓包准确得多也轻得多(见 MySQL执行计划分析 末节)。抓包的不可替代之处只有一个:不需要改数据库任何配置、不需要任何数据库权限。