Skip to content

数据库查询审计

本页面介绍 eCapture 的数据库查询审计功能,可以捕获 MySQL 和 PostgreSQL 数据库服务器执行的 SQL 查询。这些模块支持实时数据库审计,无需修改应用程序或更改数据库配置。

关于 shell 命令审计(bash/zsh)的信息,请参见 Shell 命令审计。关于通用模块架构和生命周期的信息,请参见 模块系统与生命周期


概述

eCapture 提供两个专门用于数据库查询审计的模块:

模块数据库支持版本主要钩子函数
mysqldMySQL/MariaDBMySQL 5.6/5.7/8.0, MariaDB 10.5+dispatch_command
postgresPostgreSQLPostgreSQL 10+exec_simple_query

这两个模块都使用 eBPF uprobe 技术在数据库服务器二进制级别拦截查询执行函数,在查询被分发执行时捕获 SQL 语句。与 TLS 捕获模块不同,数据库模块不需要网络流量拦截或主密钥提取——它们直接从数据库进程内存中捕获查询。

关键特性:

  • 无需数据库配置更改:无需日志配置、无需触发器、无需安装插件
  • 零应用程序影响:在数据库服务器级别捕获,而非应用程序级别
  • 原始查询捕获:捕获数据库接收到的准确 SQL 文本
  • 基于进程的过滤:可以通过 PID 或 UID 定位特定数据库实例

模块架构

以下图表展示了数据库审计模块如何融入 eCapture 架构:

来源: cli/cmd/mysqld.go:1-50, cli/cmd/postgres.go:1-46


MySQL/MariaDB 模块

支持版本

mysqld 模块支持以下数据库版本:

  • MySQL: 5.6, 5.7, 8.0
  • MariaDB: 10.5 及更新版本

该模块针对 dispatch_command 函数,这是 MySQL 服务器架构中所有 SQL 命令的入口点。

来源: cli/cmd/mysqld.go:33-36, README.md:157-158

命令行接口

bash
# 基本用法 - 自动检测 mysqld 二进制文件
ecapture mysqld

# 指定自定义 mysqld 路径
ecapture mysqld --mysqld=/usr/sbin/mysqld

# 按函数名钩子指定函数
ecapture mysqld --funcname=dispatch_command

# 按偏移量钩子(适用于剥离的二进制文件)
ecapture mysqld --offset=0x710410

# 按 PID 过滤
ecapture mysqld --pid=12345

# 将输出保存到文件
ecapture mysqld -l mysql_queries.log

配置标志:

标志简写默认值描述
--mysqld-m/usr/sbin/mariadbdmysqld/mariadbd 二进制文件路径
--offset0uprobe 钩子的偏移地址
--funcname-f(自动)要钩子的函数名

来源: cli/cmd/mysqld.go:40-43

钩子架构

MySQL 模块钩子 dispatch_command 函数,该函数会为服务器接收到的每个 SQL 命令调用:

函数签名(概念性):

c
// MySQL 内部函数(因版本而异)
bool dispatch_command(THD *thd, const COM_DATA *com_data, enum enum_server_command command)

eBPF 程序读取:

  • THD 指针:包含连接上下文的线程句柄
  • command:命令类型(COM_QUERY 用于 SQL 查询)
  • 查询字符串:从命令数据结构中提取

来源: cli/cmd/mysqld.go:36-37

版本特定注意事项

不同的 MySQL 版本可能有不同的内部结构布局。该模块通过以下方式处理:

  1. 函数名检测:自动检测 dispatch_command 符号
  2. 偏移量计算:可以为剥离的二进制文件使用 --offset
  3. 结构适配:eBPF 代码可能需要版本特定的偏移量

常见 MariaDB 路径:

  • /usr/sbin/mariadbd (MariaDB 10.5+)
  • /usr/sbin/mysqld (MySQL 5.6/5.7/8.0)

来源: cli/cmd/mysqld.go:40


PostgreSQL 模块

支持版本

postgres 模块支持 PostgreSQL 10 及更新版本。该模块针对 exec_simple_query 函数,该函数处理简单查询协议执行。

来源: cli/cmd/postgres.go:32, README.md:159

命令行接口

bash
# 基本用法 - 自动检测 postgres 二进制文件
ecapture postgres

# 指定自定义 postgres 路径
ecapture postgres --postgres=/usr/lib/postgresql/14/bin/postgres

# 钩子指定函数
ecapture postgres --funcname=exec_simple_query

# 按 PID 过滤
ecapture postgres --pid=54321

# 将输出保存到文件
ecapture postgres -l postgres_queries.log

配置标志:

标志简写默认值描述
--postgres-m/usr/bin/postgrespostgres 二进制文件路径
--funcname-f(自动)要钩子的函数名

来源: cli/cmd/postgres.go:37-38

钩子架构

PostgreSQL 模块钩子 exec_simple_query 函数,这是简单查询协议的入口点:

函数签名(PostgreSQL 内部):

c
// PostgreSQL 源码: src/backend/tcop/postgres.c
void exec_simple_query(const char *query_string)

eBPF 程序捕获:

  • query_string:指向 SQL 查询文本的指针
  • 连接元数据:PID、UID、时间戳
  • 查询长度:用于有界内存复制

来源: cli/cmd/postgres.go:32-34

PostgreSQL 协议上下文

PostgreSQL 使用多种查询执行路径:

  1. 简单查询协议:单个 SQL 语句,由 exec_simple_query 捕获
  2. 扩展查询协议:预编译语句(Parse/Bind/Execute)
  3. 快速路径接口:函数调用接口

当前模块实现专注于简单查询协议,该协议处理:

  • 来自 psql 命令行客户端的 SQL 语句
  • 来自许多客户端库的直接 SQL 执行
  • 管理命令

来源: cli/cmd/postgres.go:30-34


查询捕获机制

这两个模块都遵循类似的基于 eBPF 的捕获模式:

事件结构

这两个模块发出的事件具有类似的结构:

字段类型描述
timestampuint64事件时间戳(纳秒)
piduint32数据库服务器的进程 ID
tiduint32线程 ID
uiduint32进程的用户 ID
query_lenuint32查询字符串的长度
querychar[]SQL 查询文本

内存安全

eBPF 程序实现了几项安全措施:

  1. 有界读取:查询长度受限以避免内核栈溢出
  2. 用户内存访问:使用 bpf_probe_read_user() 进行安全访问
  3. 空终止:确保查询字符串正确终止
  4. 验证:eBPF 验证器在加载前确保内存安全

通用特性

进程过滤

这两个模块都支持标准的 eCapture 过滤选项:

bash
# 按特定 PID 过滤
ecapture mysqld --pid=1234

# 按特定 UID 过滤
ecapture mysqld --uid=1000

# 按进程名过滤(如果支持)
ecapture mysqld --pname=mysqld

输出模式

数据库审计模块以文本格式输出捕获的查询:

2024-09-15T10:30:45Z INF Query captured pid=12345 uid=999
SELECT * FROM users WHERE id = 123;

2024-09-15T10:30:46Z INF Query captured pid=12345 uid=999
UPDATE accounts SET balance = balance - 100 WHERE account_id = 456;

输出可以定向到:

  • 控制台:实时监控(默认)
  • 文件:使用 -l--logaddr 标志
  • 远程日志:使用 --logaddr 进行集中收集

来源: cli/cmd/mysqld.go:36-37, cli/cmd/postgres.go:32-34

与模块系统集成

这两个数据库模块都实现了 IModule 接口并遵循标准生命周期:

  1. Init:检测数据库二进制文件,解析函数地址
  2. Start:加载 eBPF 字节码,附加 uprobe
  3. Run:持续从 perf 缓冲区读取事件
  4. Close:分离 uprobe,清理资源

有关 IModule 接口的详细信息,请参见 模块系统与生命周期


限制与注意事项

MySQL/MariaDB 限制

  1. 二进制格式:需要函数符号(使用 --offset 可处理剥离的二进制文件)
  2. 预编译语句:在所有情况下可能无法捕获参数化查询
  3. 二进制协议:当前主要关注文本协议查询
  4. 版本特定:内部结构在不同版本之间有所不同

PostgreSQL 限制

  1. 仅限简单查询协议:不捕获扩展查询协议(预编译语句)
  2. 复制查询:可能不捕获逻辑复制查询
  3. 后台任务:可能会遗漏自动清理和其他后台查询
  4. 多语句查询:批处理中的每个语句可能单独出现

一般限制

  1. 性能影响:对高流量数据库有最小但可测量的开销
  2. 查询截断:根据缓冲区大小,非常长的查询可能会被截断
  3. 二进制数据:查询中的 BLOB/二进制数据可能无法正确显示
  4. 编码:假设 UTF-8 或 ASCII 查询编码

安全注意事项

  • 敏感数据暴露:捕获的查询可能包含密码、PII
  • 需要 Root 权限:eBPF 需要 CAP_BPF 或 root
  • 审计日志:应使用适当的文件权限保护
  • 合规性:确保查询日志记录符合数据保护法规

与传统审计方法的比较

特性eCapture eBPF数据库日志应用程序日志
配置需要配置需要代码更改
性能影响最小中等变化
查询覆盖所有查询可配置取决于实现
设置时间立即需要重启需要部署
敏感数据捕获所有内容可配置应用程序控制
开销位置内核数据库应用程序

来源: cli/cmd/mysqld.go:1-50, cli/cmd/postgres.go:1-46, README.md:157-159


使用示例

MySQL 示例

bash
# 启动 MySQL 查询捕获
sudo ecapture mysqld --mysqld=/usr/sbin/mysqld -l /var/log/mysql_audit.log

# 输出示例:
# 2024-09-15T10:30:45Z INF Query captured pid=12345 uid=999
# SELECT user, host FROM mysql.user;
#
# 2024-09-15T10:30:46Z INF Query captured pid=12345 uid=999  
# CREATE DATABASE testdb;

PostgreSQL 示例

bash
# 启动 PostgreSQL 查询捕获
sudo ecapture postgres --postgres=/usr/lib/postgresql/14/bin/postgres -l /var/log/postgres_audit.log

# 输出示例:
# 2024-09-15T11:15:22Z INF Query captured pid=54321 uid=109
# SELECT * FROM pg_stat_activity;
#
# 2024-09-15T11:15:23Z INF Query captured pid=54321 uid=109
# EXPLAIN ANALYZE SELECT * FROM large_table WHERE id > 1000;

结合进程过滤

bash
# 仅审计特定数据库实例
sudo ecapture mysqld --pid=$(pgrep -f "mysqld.*port=3307")

# 仅审计来自特定用户的查询
sudo ecapture postgres --uid=1000

来源: cli/cmd/mysqld.go:1-50, cli/cmd/postgres.go:1-46

数据库查询审计 has loaded