mysql general_log 不记录真实参数值,因其仅保存语句文本,参数化查询中的 ? 或 $1 不会被替换;需结合应用层日志或 performance_schema.events_statements_history_long(mysql 8.0+)还原实际输入。

MySQL general_log 里为什么看不到真实参数值
因为 general_log 只记录语句文本,不展开绑定变量。拼接 SQL(如 "SELECT * FROM users WHERE id = '" + input + "'")会原样记下恶意内容,但参数化查询(如 SELECT * FROM users WHERE id = ?)只留占位符。你看到的 ? 或 $1 后面没有值,不是日志漏了,是 MySQL 本来就不记录——它不认为那是“语句的一部分”。
想还原实际输入,必须搭配应用层日志(比如 Spring 的 PreparedStatement 执行前打点)或启用 performance_schema.events_statements_history_long(MySQL 8.0+),但后者默认关闭且需提前调大 performance_schema_events_statements_history_long_size。
PostgreSQL pgaudit 日志中 client_addr 总是 pgbouncer IP 怎么办
这是常见溯源断链:pgbouncer 默认把所有连接伪装成自己,client_addr 固定为它的地址,查不到真实攻击源。修复不是改 pgaudit,而是改 pgbouncer 配置。
- 在
pgbouncer.ini中加ignore_startup_parameters = application_name - 应用侧用 JDBC 连接时显式设
application_name=web-login-service,或在 HTTP 请求头里传X-Real-IP并由网关注入到 connection string - 确保
pgaudit.log_client = on,否则连客户端信息都不记
用 grep 扫描日志识别注入为什么总漏报
正则匹配 "union.*select|sleep\(\)|--" 看似直接,但攻击者早有对策:大小写混用(UnIoN)、注释变形(/*foo*/、/**/)、空格替换为 tab 或 +、甚至十六进制编码(0x554e494f4e)。这些都会让简单正则失效。
更可靠的做法是:
- 先用
mysql-audit-log-parser(MySQL 官方)或pgauditlogtojson(PostgreSQL 社区)把原始日志转成结构化 JSON - 再对
sql_text字段做模糊匹配,比如用re.search(r'(union\s+select|sleep\s*\(|extractvalue)', text, re.I | re.S) - 重点看异常组合:同一 IP 在 1 秒内连续查
information_schema和业务表,或SELECT后跟明显非业务字段(如@@version,user())
审计日志本身会不会成为新的风险点
会。审计日志文件若权限宽松、明文存储、和数据库同机部署,就等于把攻击证据亲手递给入侵者。MySQL 的 audit.log 默认属主是 root:root,普通监控进程读不了;PostgreSQL 的 CSV 日志若放在 /var/lib/postgresql/data/pg_log/,DBA 账号删起来毫无压力。
必须做到:
- MySQL:
audit_log_file指向非 root 可写路径,且设audit_log_rotate_on_size防止撑爆磁盘 - PostgreSQL:用
logging_collector = on+log_directory指向独立挂载卷,禁止 DBA 访问该目录 - 所有审计日志最终要落库到权限隔离的 OSS/Bucket,且禁用
DeleteObject权限
真正难的不是“怎么开日志”,而是确保日志从产生、传输、解析到告警的每一步,都和被审计对象物理或逻辑隔离——否则你盯着日志看半天,攻击者早就删完跑路了。











