Nginx 与 Apache 配置取证

没有 Web 服务器配置,手里的日志和文件就是一堆没有坐标的碎片。

最后更新 2026-10-09版本 v1.0维护 DigiForensics查看历史 0

关键词:nginx.conf、vhost、log_format、access_log、proxy_pass、.htaccess、mod_rewrite、server-status 难度:进阶 前置知识:HTTP 基础、Linux 目录结构、反向代理概念、日志轮转机制

一、概述

没有 Web 服务器配置,手里的日志和文件就是一堆没有坐标的碎片。

站点目录在哪、日志写到哪、请求转到哪、某个 location 是不是压根不记日志——四个问题只在配置文件里有答案。

跳过这一层直接分析 access.log,你连自己在读哪台机器的哪一段时间都说不清,更解释不了为什么关键时段「什么都没有」。

要做的是一张「路由图」:把散落的工件归位到具体的站点、路径和日志文件上,顺带把配置本身当证据读。

在取证流程中的位置:这一层自己几乎不产出结论,却决定后面所有分析是否成立。站点映射错了,第 02 篇的日志分析跟着全错;access_log 路径找错了,「无访问记录」就是个伪结论。上游依赖 取证工件地图(Linux 侧) 的目录清单与 系统日志深度分析 的轮转认知;下游支撑网站目录取证(第 2 步)、访问日志分析(第 4 步)和报告里的归因表述。

涉及的证据形态——具体到文件,不是「配置文件」这种大类:

形态 位置 取证价值
Nginx 主配置 /etc/nginx/nginx.conf、/www/server/nginx/conf/nginx.conf(宝塔) ★★★★★ 生效配置的入口
站点配置 conf.d/*.conf、conf/vhost/nginx/*.conf、sites-available/ + sites-enabled/ 软链 ★★★★★ 站点与日志映射
Apache 主配置 /etc/apache2/apache2.conf、/etc/httpd/conf/httpd.conf ★★★★
log_format 定义 主配置内,可多个 ★★★★★ 决定字段顺序
.htaccess 站点目录逐级向上查找 ★★★★ 隐藏重写规则
mods-enabled/ Apache 模块加载状态 ★★★ 决定 .htaccess 是否生效
访问/错误日志 /var/log/nginx/、/www/wwwlogs/(面板环境) ★★★★★
在线状态页 /server-status?auto、/server-info ★★★★★ 活证据(需授权窗口)

⚠️ 本篇最容易被忽略的一点:面板环境的路径与源包完全不同。 源包装在 /etc/nginx、日志在 /var/log/nginx,宝塔装在 /www/server/nginx/conf、日志在 /www/wwwlogs。「在 /var/log/nginx/ 没找到日志」和「这个站点没有日志」是两件完全不同的事,见 4.2 字段与路径判读。

能回答什么:哪个配置文件真正生效;每个站点的 root / DocumentRoot 实际指向哪里;哪个 location 的日志被关掉了、日志文件在哪;proxy_pass 背后的内部拓扑,以及真实客户端 IP 能不能采信;alias 错配、auto_prepend_file、.htaccess 构成的配置层攻击面;某时段日志缺失是轮转、删除还是压根没记。

注意配置层与 PHP 层的分界:篡改机制的核心可能就写在 .user.ini 里,而它不是 Nginx 配置——只读 nginx.conf 会把最关键的证据整个漏掉。

不能回答什么:请求体内容、文件上传内容、命令执行结果,配置层看不到这些。攻击者实际改了哪个文件,要靠文件时间戳与内容去比。.htaccess 里写了什么不等于生效的行为,它受 AllowOverride 和模块加载约束。至于在生产中的被入侵主机上执行 nginx -T 拿全量配置——它会实际加载配置,见 4.1 定位与生效。

读者前提:HTTP 请求/响应结构、Linux 目录树与权限模型、进程与端口的基本认知,以及 取证流程与原则 里的哈希固定与只读挂载要求。Windows 侧的等价工件(W3C 字段顺序、IIS 站点配置)见 IIS 配置与日志,两边的字段顺序可以对照着记。

二、核心原理

2.1 配置文件的组织方式

Nginx 的配置是层级化的:main → http → server → location。取证时必须理解「上下文」,同一个指令在不同层级含义完全不同。

发行版/安装方式 主配置 站点配置目录 站点根目录 日志目录
apt / yum 安装 /etc/nginx/nginx.conf conf.d/*.conf /usr/share/nginx/html 或自建 /var/log/nginx/
源码编译(官方 tar 包) /usr/local/nginx/conf/nginx.conf conf/extra/、conf/ 下直接写 编译时指定 同上
宝塔生成 /www/server/nginx/conf/nginx.conf conf/vhost/*.conf /www/wwwroot/<site>/ ** /www/wwwlogs/**
宝麒麟等面板 路径随版本 conf/vhost/ /www/wwwroot/ /www/wwwlogs/
Debian 惯例 /etc/nginx/nginx.conf sites-available/ + sites-enabled/ 软链 /var/www/html /var/log/nginx/
Apache (Debian) /etc/apache2/apache2.conf sites-available/ + sites-enabled/ /var/www/html /var/log/apache2/
Apache (RHEL/CentOS) /etc/httpd/conf/httpd.conf conf.d/*.conf /usr/share/httpd/html /var/log/httpd/

⚠️ sites-enabled/ 只是软链接目录。 取证时要看软链接指向谁,而不是只看 sites-enabled/ 里有什么——攻击者或运维可能直接改了 sites-available/ 下的原文件,也可能有孤儿配置(available 里有、enabled 里没链,属未生效)。

最关键的一步:拿到真正生效的完整配置。

# 方式一:从镜像里静态找(不执行任何东西,最安全)
find /mnt/df -maxdepth 5 -name 'nginx.conf' -o -name 'httpd.conf' \
     -o -name 'apache2.conf' 2>/dev/null

# 方式二:确认主配置里 include 了什么(读文件,不执行)
grep -nE '^\s*include' /mnt/df/etc/nginx/nginx.conf

# 方式三:在目标副本上执行,输出全量生效配置(生产主机上禁止)
nginx -T 2>&1 | head -100
apachectl -S 2>&1 | head -40      # 列出 vhost 与 DocumentRoot 对应关系

2.2 log_format 字段详解

这是本篇最容易被搞错、也最影响后续分析的部分。 Nginx 的 combined 格式定义如下:

log_format combined '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" "$http_user_agent"';

逐字段拆解(注意 $remote_addr 之后是 -,再之后是 $time_local,$remote_user 在方括号内):

序 字段 含义 取证用途
1 $remote_addr 客户端 IP(TCP 对端)。若前置了反向代理,这里是代理 IP ★★★★★ 定位攻击源
2 - 字面连字符,不是字段 固定占位
3 $remote_user HTTP Basic/Digest 认证用户名,未认证时为 - 区分认证访问
4 $time_local 本地时区时间,格式 [18/Mar/2024:02:14:33 +0800] ★★★★★ 时间线基准
5 $request 完整请求行:"GET /a.php?x=1 HTTP/1.1" ★★★★★ 还原行为
6 $status 响应状态码 200 成功、403 禁目录、404 不存在、500 脚本错误
7 $body_bytes_sent 响应体字节数(不含响应头) 估算上传/下载量
8 $http_referer Referer 头,无则 - 判断是否同源;WebShell 请求常无 Referer
9 $http_user_agent UA,无则 - 工具指纹

🔴 高频错误提醒:$remote_addr 之后是字面 -,再之后是 $time_local。很多解析脚本错写成「IP → 时间 → 用户名」的顺序,或者把方括号里的时间当成第 3 字段。解析 combined 日志必须按上面的完整结构来做,第 7 篇和本节的脚本都严格遵守此顺序。

其他需要认识的格式:

格式名 字段 特点
main $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent 无 referer/UA
combined 上表 9 项 默认,最常用
自定义 dlog 常见加 $request_time、$upstream_response_time、$server_protocol 自定义格式可能没有 UA/referer,也可能含 cookie——务必逐台核对

⚠️ 取证时必须核对 log_format 的实际定义,不能假设一定是 combined。一台机器可能有多个 log_format 定义,多个 access_log 引用不同格式。用 grep -n 'log_format' nginx.conf 逐一列出。

2.3 关键指令的取证意义

root vs alias(最危险的一类配置错误)

两者的路径拼接规则不同,这是很多「任意文件读取」漏洞的根因:

location /static/ { alias /data/files/; }
# 请求 /static/../etc/passwd → /data/files/../etc/passwd → /data/etc/passwd
# 若写成 root /data/files;(无尾斜杠),/static/x → /data/files/static/x,语义完全不同
指令 语义 取证关注点
root 替换 location 前缀 拼错会导致访问到计划外目录
alias 整段替换 location 前缀 少写尾斜杠或写错路径极易造成目录穿越,务必逐个核对
try_files 依次尝试文件/目录 $uri$uri/ 落到目录可能触发索引列表泄露

access_log off —— 痕迹消失开关

access_log off 本身不是恶意证据(很多站点用它降压),但它的取证意义是:该 location 下的行为没有 Nginx 侧记录。此时必须转向 PHP 层(/www/wwwlogs/ 面板日志、PHP-FPM 日志)或数据库审计。** proxy_pass —— 看清真实拓扑**

location /api/ { proxy_pass http://127.0.0.1:8080/; }     # 末尾有斜杠:会剥掉 /api/ 前缀
location /api/ { proxy_pass http://127.0.0.1:8080;  }     # 末尾无斜杠:路径传递方式不同!
观察 意义
proxy_pass 指向 127.0.0.1:某端口 有内部服务,可查该端口的进程与日志
指向内网 IP 存在跨主机调用,内网主机可能也在检材范围内
上游转发明文 Authorization 或 Cookie 凭据可能被写入下游日志
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for 后端日志里的是真实 IP,取证时可直接采信
未设置该头 后端日志里全是代理 IP,不能直接当作客户端 IP
sub_filter_types text/html;
sub_filter 'https://cdn.old.com' 'https://cdn.new.com';   # 内容替换
add_header 'X-Powered-By' 'PHP/5.6.40';                    # 伪造版本号,干扰技术栈判断

add_header 伪造版本号在真实入侵中出现过——它让基于响应头的技术栈判断失效,取证时要意识到这一点。

Apache 侧:mod_rewrite 与 .htaccess

# 全局重写规则
RewriteRule ^/old/(.*)$ /$1 [R=301,L]

# .htaccess 隐藏规则(放在任意目录,逐级向上查找)
RewriteEngine On
RewriteRule ^\.(git|svn|env) - [F]        # 禁访问版本库
RedirectMatch 404 ^/backups?/            # 隐藏备份目录
  1. [R=301,L] 通常是管理员主动做的跳转;[R=302] 出现在备份路径上,可能是后门在给自己打掩护。
  2. .htaccess 是逐级向上查找的:请求 /a/b/c.php 时 Nginx 不读 .htaccess,但 Apache 会依次检查 /a/b/、/a/、/。必须逐层找,不能只查站点根目录。
  3. mod_rewrite 生效需要 AllowOverride All;若为 None,站点目录里的 .htaccess 完全无效——此时「配置看起来在禁某目录」实际是失效的假象。

2.4 Apache 特有工件的取证价值

文件/接口 内容 价值
/var/log/apache2/access.log 访问日志(格式同 Nginx) ★★★★★
/var/log/apache2/error.log 错误日志,含 PHP 警告与栈 ★★★★
/var/log/apache2/other_vhosts_access.log 多 vhost 时自动追加,容易被忽略 ★★★★
/var/log/apache2/access.log.*.gz 轮转历史 ★★★★
server-status(/server-status?auto) 活动连接、正在处理的请求、子进程 PID ★★★★★ 在线时价值极高
server-info 模块列表与完整配置 ★★★★
/etc/apache2/mods-enabled/ 启用了哪些模块 判断 mod_rewrite/mod_php 是否真能用
/etc/apache2/sites-enabled/ 软链接,要 ls -l 看指向 ★★★★★

server-status 在取证中的特殊价值:它实时列出当前正在处理的请求 URL、来源 IP、子进程 PID。若取证窗口内还能访问,可以直接抓到攻击者正在进行的操作——这是任何离线日志都给不了的活证据。但这必须在委托方明确授权的在线取证窗口内进行,且要立即截图固定。

Windows 侧的同类配置(站点配置、日志目录、W3C 字段顺序)见第 02 篇,字段语义可以对照着看。


三、操作步骤

3.1 第 1 步:定位并固定配置

# 1) 找到所有候选主配置
find /mnt/df -maxdepth 5 \( -name nginx.conf -o -name httpd.conf \
     -o -name apache2.conf \) 2>/dev/null | tee /evidence/ws-conf-list.txt

# 2) 完整复制并固定哈希(先复制,再分析)
mkdir -p /evidence/nginx-conf /evidence/apache-conf
cp -rp /mnt/df/etc/nginx /evidence/nginx-conf/ 2>/dev/null
cp -rp /mnt/df/etc/apache2 /evidence/apache-conf/ 2>/dev/null
cp -rp /mnt/df/etc/httpd  /evidence/apache-conf/ 2>/dev/null
find /evidence/nginx-conf /evidence/apache-conf -type f \
     -exec sha256sum {} + > /evidence/ws-conf-hashes.txt

# 3) 看主配置的 include 关系
grep -nE '^\s*include' /evidence/nginx-conf/etc/nginx/nginx.conf 2>/dev/null

# 4) 配置文件的修改时间(判断是否被改过的关键)
find /evidence/nginx-conf /evidence/apache-conf -type f \
     -printf '%T+ %p\
' 2>/dev/null | sort | tail -20
# 或: find ... -name '*.conf' -newermt '2024-03-01' -ls

3.2 第 2 步:提取站点与日志映射

# 所有 server 块、root、access_log、error_log 一次看全
grep -rnE 'server_name|root |alias |access_log|error_log|proxy_pass' \
     /evidence/nginx-conf/etc/nginx/ 2>/dev/null

# Apache 侧
grep -rnE 'ServerName|ServerAlias|DocumentRoot|ErrorLog|CustomLog' \
     /evidence/apache-conf/ 2>/dev/null
ls -l /evidence/apache-conf/etc/apache2/sites-enabled/ 2>/dev/null   # 看软链接指向

把结果整理成映射表,这是后续所有分析的基准:

域名 root 实际路径 access_log 路径 是否 off 备注
digiforensics-site /www/wwwroot/digiforensics-site/ /www/wwwlogs/digiforensics-site.log 否 主站
cdn.digiforensics /data/cdn/ off 是 ★ 无访问记录

3.3 第 3 步:查隐藏规则、危险配置与站外目录

# .htaccess 逐级找(Apache 才会生效)
find /mnt/df/www/wwwroot /mnt/df/var/www -name '.htaccess' 2>/dev/null
find /mnt/df/www/wwwroot -name '.htaccess' -exec grep -HnE 'Rewrite|Redirect|php_value|auto_prepend' {} + 2>/dev/null
grep -rn 'AllowOverride' /evidence/apache-conf/ 2>/dev/null     # 决定 .htaccess 是否真生效

# Nginx 侧的隐藏规则
grep -rnE 'Redirect|return 40[13]|deny' /evidence/nginx-conf/ 2>/dev/null | head -30

# .user.ini(PHP 层面劫持,详见第 06 篇)
find /mnt/df/www/wwwroot -name '.user.ini' -exec grep -Hn . {} + 2>/dev/null

# 站外目录:面板装的站点有时把路径指到 /www/wwwroot 之外,
# 这类"计划外目录"是篡改网站改 JS 的常见落点
grep -rnE '^\s*(root|alias) ' /evidence/nginx-conf/etc/nginx/ 2>/dev/null \
  | grep -v '/www/wwwroot' | grep -v '/usr/share/nginx'
ls -la /mnt/df/data/ 2>/dev/null      # 结果需逐个核实:是否该存在?内容是否像正常业务?

3.4 第 4 步:解析访问日志与核查轮转

awk -F'"' 是 combined 日志的实用技巧:把行按双引号切成 3 段,$1 是「IP - user [time]」,$2 是请求行,$3 是 referer + UA。不需要写正则就能可靠地拿到请求行。

Z=/mnt/df/www/wwwlogs/digiforensics-site.log
awk -F'"' '{split($1,a," "); print a[1], $2}' "$Z" | sort | uniq -c | sort -rn | head -20
# 关键:按 IP + 路径组合看,能发现"只对某个 URL 反复访问"的扫描/后门行为

服务还在跑的话,日志随时会被轮转覆盖——这是第 01 篇强调的抢时间理由,在 Web 取证里要具体到文件层面: