核心是通过统一x-request-id实现全链路安全事件关联:nginx用$request_id生成唯一id,透传至后端/waf,各组件(modsecurity、限流、应用)均以该字段记录日志,确保格式一致;再基于id聚合分析攻击链,命令行即可快速验证与初筛。

核心是让每一次请求都带一个唯一、可信、全程可见的 ID,再把所有安全事件(WAF 拦截、限流拒绝、SQL 注入告警、403/429 错误等)都和这个 ID 对齐。不靠 ID 关联,日志就是一堆碎片。
确保 X-Request-ID 全链路透传
Nginx 的 $request_id 是天然锚点——它在请求刚进来时就生成,客户端无法伪造,格式统一(32 位小写 hex UUID),且生命周期覆盖整个处理过程,包括被拒绝或超时的请求。
- 在
http或server块中定义日志格式,强制落盘:log_format security '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_id $upstream_http_x_request_id'; - 在
location中透传给后端和 WAF:proxy_set_header X-Request-ID $request_id; - 对被拦截的请求(如
auth_request子请求、ModSecurity 拒绝),开启log_subrequest on;并调高error_log级别(如debug),确保 ID 出现在 error_log 中
让所有安全组件输出同一字段名和格式
审计失效,90% 是因为各模块用的 ID 字段名不一致、格式不统一,导致无法关联。
- ModSecurity 审计日志:启用
SecAuditLogParts ABIJEFHZ,确保响应头 H 包含X-Request-ID;用SecAuditLogRelevantStatus "^(?:4|5)\d\d$"只记录异常响应 - 自定义风控模块或限流逻辑:在日志中显式打印
X-Request-ID,不要用req_id、trace_id等别名 - 后端应用日志:接收并透传
X-Request-ID,在所有业务、错误、审计日志中作为固定字段输出
基于 request_id 聚合分析异常事件
拿到统一 ID 后,就能把原本孤立的线索串成一条攻击链。
- 查一次 SQL 注入拦截:从 ModSecurity audit log 中提取
X-Request-ID: abc123...,再用它去 access.log 查原始请求头、IP、UA、完整 URL - 定位高频扫描源:统计 access.log 中相同
$request_id出现多次(说明重试或工具轮询),再聚合这些 ID 对应的 IP 和路径 - 还原攻击全貌:一个 429(限流)+ 一个 403(WAF 拦截)+ 一个 500(后端报错)若共享同一个
$request_id,基本可断定是同一次恶意请求触发的多层防御响应
用命令行快速验证与初筛
不需要上平台,几条命令就能确认 ID 是否生效、异常是否可追溯。
- 确认 access.log 中有 ID:
tail -n 10 /var/log/nginx/access.log | grep -oE '[a-f0-9]{32}' | head -1 - 找最近 5 分钟所有被 WAF 拦截的请求 ID:
grep 'ModSecurity: Access denied' /var/log/modsec/audit.log | grep -oE 'X-Request-ID: [a-f0-9]{32}' | cut -d' ' -f2 - 反查这些 ID 在 access.log 中的原始请求:
awk '{print $NF}' /var/log/nginx/access.log | grep -Ff - /var/log/nginx/access.log(需先保存 ID 到临时文件)











