搜索全站

输入至少 2 个字符,查找相关内容。

↑↓ 选择 Enter 打开Esc 关闭

CDN 识别与源站挖掘

CDN 给网站带来性能与防护,也给取证制造了一个具体的麻烦:日志里的 IP 既不是真实访问者,也不是服务器本身。

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

⚖️ 本篇的边界(重要)

源站挖掘是取证中的合法技术手段,前提是已获得委托方授权,目标是找回自己委托资产的真实源站,用于还原完整请求链路。

本篇不涉及:对他人未授权的资产进行源站探测、绕过访问控制、扫描不属于委托范围的 IP 段。任何探测动作都必须在委托方明确的资产清单范围内进行,并遵守目标所在司法辖区的规定。

一、概述

CDN 给网站带来性能与防护,也给取证制造了一个具体的麻烦:日志里的 IP 既不是真实访问者,也不是服务器本身。

站点接了 CDN 之后,access_log 里记录的是边缘节点的地址。

这时候你手里的日志看起来是"完整的",实际上它缺了最关键的一段链路——那个域名背后,那台真实的服务器在哪里?

为什么非找到它不可:

若只有 CDN 侧证据 若找到源站
只能看到边缘节点日志 能看到完整请求,包括 CDN 未缓存的 POST
无法确定哪些路径被真正访问过 能核对两边日志,找出绕过 CDN 的直连
无法判断源站是否还留着 WebShell 能直接检查源站文件系统——这通常才是案件关键
攻击者绕 CDN 直连源站的攻击在 CDN 侧完全不可见 直连攻击完整记录在源站日志里

最关键的一点:CDN 缓存只加速 GET,POST 与动态请求通常回源。

攻击者上传 WebShell 用的正是 POST——这类请求在 CDN 侧往往只留下一行转发记录,真正的内容只有源站知道。

而绕过 CDN 直连源站的攻击,在 CDN 侧连一行记录都不会有。

在取证流程中的位置:本篇属于分析环的链路补全层,通常在文件层与流量层都卡住时启用——"日志里只有 CDN IP"是最常见的归因中断点。

上游依赖 Nginx 与 Apache 配置取证 对 $remote_addr 语义与 proxy_set_header 的分析。

下游直接接上源站侧的全部工作(取证工件地图、WebShell 排查、流量分析),见 3.5 第 5 步。

涉及的证据形态:

形态 位置 / 字段 取证价值
DNS 解析链 dig 的 CNAME 链、+noall +answer ★★★★★ CNAME 指向 *.cdn.* / *.edge.* 即已接 CDN
HTTP 响应头 server、via、x-cache、cf-ray、age ★★★★ 二次确认
CT log 证书透明度日志,公开可查、带时间戳、可哈希固定 ★★★★★ 源站回溯的主力手段
未接 CDN 的子域 测试站、静态站、旧站、API 站的 A 记录 ★★★★★ 最常见的突破口
历史 DNS 接入 CDN 之前的解析记录 ★★★★ 直连攻击的原始来源
配置错误枚举 未设 Host 白名单、遗留测试域名、mail/origin 等命名习惯 ★★★
源站指纹 自定义错误页、序列号、静态资源哈希、响应头顺序 ★★★★ 验证候选的关键
CDN 与源站日志的差集 两侧记录的比对结果 ★★★★★ 找出绕过 CDN 的直连

🔴 取证价值最高的一条判据:同一域名下未接 CDN 的子域常常直接暴露源站。 运维往往只把主站接入 CDN,测试站、静态站、旧站、API 站常常遗漏。但同时要记住边界:找到了源站 IP 不等于它就是攻击入口——IP 归属 CDN 厂商、或属于托管在同一台机器上的其他客户,都需要单独认定,见 4.1 归属判断。

这个域名背后哪台是真实服务器;哪些子域没走 CDN;源站上是否还留着后门;绕 CDN 直连源站的攻击有没有发生过;源站 IP 上还托管着哪些其他域名;CDN 配置是否留下了绕过路径(未设 IP 白名单、未配 SNI 校验等)。

CDN 边缘节点缓存了什么、什么时候缓存的——这需要 CDN 厂商侧的配合或控制台权限。

未签发过证书的源站(CT log 里查不到)只能靠其他路径。

源站机器的内部情况?找到 IP 只是拿到入口,文件系统证据必须在授权范围内获取。

源站 IP 上其他客户域名的内容——超出委托范围就是越界,见 4.3 范围与授权边界。

内容边界(本篇的红线,已在篇首声明):源站挖掘是取证中的合法技术手段,前提是已获得委托方授权,目标是找回自己委托资产的真实源站,用于还原完整请求链路。本篇不涉及对他人未授权资产的源站探测、绕过访问控制、扫描不属于委托范围的 IP 段。任何探测动作都必须在委托方明确的资产清单范围内进行,并遵守目标所在司法辖区的规定。

读者前提:需要 DNS 解析原理与 CNAME 链结构、TLS 握手与证书基本概念(SNI、证书 SAN)、HTTP 响应头语义、以及反向代理的基本模型。

反向代理如何在日志里隐藏真实 IP 的机制见 第 1 篇 的 2.3 节。

建议先按 3.1 第 1 步 确认 CDN 状态再往下——如果本来就没接 CDN,本篇的多数手段不适用。

二、核心原理

2.1 CDN 是怎么改变日志的

一个典型的 CDN 架构:

用户 ──► CDN 边缘节点 ──► 源站服务器
         (缓存 GET)        (所有请求最终都会到这里)
请求类型 CDN 行为 边缘节点日志 源站日志
GET 静态资源,缓存命中 不回源 有记录 无记录
GET 动态页面,未命中 回源 有记录 有记录
POST(登录、上传、执行) 总是回源 有记录 有完整记录
攻击者直连源站(绕过 CDN) 不经过 CDN 无记录 有完整记录

由此推出两条取证推论:

  1. 源站日志是"必然存在"的——只要 CDN 正常工作并转发,源站一定有日志。所以拿到源站 IP 几乎一定能拿到更完整的日志。
  2. CDN 侧日志里的 4xx/5xx 与状态码可能被 CDN 自己改写(CDN 配置了错误页缓存时尤其明显),不能把 CDN 日志的状态码当作源站真实响应。

2.2 CDN 识别:DNS 链

最可靠的第一手判据是 DNS 解析结果。

接入 CDN 时,域名通常不再是 A 记录直指服务器,而是 CNAME 到 CDN 厂商的域名。

dig +short digiforensics-site
# cdn.digiforensics-edge.example.net.        ← CNAME,说明接了 CDN

dig digiforensics-site CNAME +short
# cdn.digiforensics-edge.example.net.

# 完整链路
dig digiforensics-site +noall +answer
# digiforensics-site.		300	IN	CNAME	cdn.digiforensics-edge.example.net.
# cdn.digiforensics-edge.example.net. 60 IN A	203.0.113.20
现象 判断
CNAME 指向 *.cdn.* / *.edge.* / *.cloudfront.net / *.alicdn.com 等 接了 CDN
A 记录直接指向单个 IP,且该 IP 属于云厂商 可能是独立主机,也可能直连云厂商网络
A 记录返回多个 IP 可能 CDN,也可能是DNS 轮询(多台服务器)——需看 IP 归属
域名下部分子域有 CNAME、部分没有 典型的"CDN 接入不全",未接 CDN 的子域往往直达源站 ★

🔴 取证价值最高的一条:同一个域名下,未接 CDN 的子域常常直接暴露源站。 运维往往只把主站接入 CDN,测试站、静态站、旧站、API 站常常遗漏。这往往是突破口。

2.3 CDN 识别:HTTP 响应头

curl -sI https://digiforensics-site/ | head -30
响应头 指向
Server: cloudflare Cloudflare
X-Cache: HIT / MISS / EXPIRED 多种 CDN(Cloudflare、阿里云等)
Via: 中出现 1.1 vegur(蓝汛)、ali-swift-* 阿里云 CDN
X-Swift-Cache 又拍云等
X-Cache-Lookup 快网等
Age: 3600 任意 CDN,表示该响应已缓存 3600 秒 ★
cf-ray、cf-cache-status Cloudflare
X-Cache: TCP_HIT 腾讯云
Timing-Allow-Origin 各类 CDN
完全没有上述头 可能是直连源站(★ 这本身就是源站线索)

即使厂商换了自定义头,Age: <秒数> 通常还在。

2.4 CT log(证书透明度日志)—— 源站回溯的主力手段 ★

这是当前最实用的源站回溯方法,本节重点展开。

原理

自 2017 年前后起,主流 CA 与主流浏览器逐步把公开受信任 TLS 证书的 CT 记录作为签发前提(Apple、Google、Mozilla 的证书政策要求、以及 2018 年 4 月起 Chrome 对 EV 证书的硬性要求,都推动了这件事)。

全球有多个公开的 CT log 汇总这些记录,例如 Google CT Log,以及 crt.sh 这类按域名聚合的第三方检索服务。

因此在绝大多数公开网站场景下,一张受信任证书的签发几乎必然留下 CT 痕迹。

但“几乎必然”不等于“绝对覆盖”——自签证书、内网私有 CA 签发的证书、以及历史上绕过该要求的签发,都可能查不到。

关键点:签发证书时必须提交所有 SAN 域名的公钥信息到 CT log,且无法事后删除。

由此得到取证的用途:

任何曾经为这个域名签发过证书的人,其证书上的所有域名都被公开记录了。 包括:源站常用的 origin.example.com、direct.example.com、内部测试域名、绑定在同一个 IP 上的其他客户域名。

取证的直接价值:

  1. 得到可用于回溯 IP 的域名线索 —— CT 本身不提供 IP,但它给出的 origin. direct. test. old. dev. admin. 这类非常规子域名,是后续做 DNS 历史查询、证书下载比对、目标验证的高价值抓手。IP 最终要靠 DNS 历史、SNI 探测和日志交叉验证才能确定。
  2. 得到源站专用子域名 —— origin. direct. oss. static-origin. 这类命名往往是内部用词。
  3. 得到未公开的业务域名 —— 同一客户的其他站点。
  4. 得到时间线 —— CT 记录含"证书签发时间",可与入侵时间对照。

这四条里,第 1 条最关键——它不是直接给出答案,而是把搜索空间从"整个互联网"缩小到"十几个名字"。

⚠️ 重要局限(必须在报告里写明):

  • CT log 只记录证书,不记录 IP 指向。 它告诉你"这个域名签过证书",但域名现在解析到哪,CT 不管。
  • 未使用公开 CA 证书的源站(自签证书、IP 直连无证书)不会出现在 CT log。
  • CT 记录与“未来”无关,回溯靠的是 log 自身保留的历史。 公开 CT log 自 2013 年前后开始运行并长期保留条目,因此历史证书在多数情况下可以查到;但保留策略、log 迁移与部分早期条目缺失都可能导致查不到,必须以实际查询结果为准。
  • 查询需要联网。在离线或隔离的内网取证环境中无法使用,必须提前在有网环境采集并固定为证据。

实操查询

# 方式一:crt.sh(按域名聚合的 CT 记录)
# 注意:URL 中的 %25. 是对子域名通配的转义写法
curl -s 'https://crt.sh/?q=%25.digiforensics-site&output=json' \
  | python3 -c "import json,sys; d=json.load(sys.stdin); print('\
'.join(sorted({n for r in d for n in r['name_value'].split()})))"

# 方式二:Google CT Log API(官方原始记录)
curl -s 'https://transparencyreport.google.com/transparencyreport/api/v3/httpsreport/ct/v1/logs' -o /dev/null   # 探活
# 实际查询通常通过官方前端;离线取证时建议直接采集 crt.sh 结果并固定

# 方式三:证书指纹反查(已知证书序列号时)
# crt.sh 支持 ?sha256=<证书指纹> 查询同一张证书的所有 SAN

查询结果要做的事:

# 1) 落盘为证据(★ 关键:CT 是联网查询结果,必须当场固定,否则不可复现)
mkdir -p /evidence/ct
date -u '+%Y-%m-%dT%H:%M:%SZ' > /evidence/ct/collected-at.txt
curl -s 'https://crt.sh/?q=%25.digiforensics-site&output=json' \
  -o /evidence/ct/crt-$(date -u +%Y%m%dT%H%M%SZ).json
sha256sum /evidence/ct/crt-*.json | tee /evidence/ct/hashes.txt

# 2) 提取去重域名
python3 -c "
import json,glob,sys
f=sorted(glob.glob('/evidence/ct/crt-*.json'))[-1]
d=json.load(open(f))
names=sorted({n.strip() for r in d for n in r.get('name_value','').splitlines()})
for n in names: print(n)
" | tee /evidence/ct/domains.txt

# 3) ★ 筛出"源站味"的子域
grep -iE 'origin|direct|backend|server|node|host|panel|admin|^\*|raw|src|oss|upstream|inner' /evidence/ct/domains.txt

# 4) 逐个解析,看哪些没走 CDN
while read -r d; do
  [ "$d" = "*.digiforensics-site" ] && continue
  ip=$(dig +short "$d" A | head -1)
  [ -n "$ip" ] && echo "$d  ->  $ip"
done < /evidence/ct/domains.txt

筛出来的典型结果:

域名 解析结果 判断
www.digiforensics-site CNAME → CDN 走 CDN
origin.digiforensics-site A → 198.51.100.24 ★ 源站
api.digiforensics-site A → 198.51.100.24 源站(同 IP)
old.digiforensics-site 无解析 历史域名,可能已下线

2.5 其他源站挖掘路径

历史 DNS

域名在接入 CDN 之前的解析记录,或曾经被解析到源站的那段时期,都可能留在历史数据库里。

# 查权威 NS 与 SOA(先确认 DNS 托管方)
dig +short digiforensics-site NS
dig digiforensics-site SOA +noall +answer
# digiforensics-site. 3600 IN SOA ns1.provider.example. admin.provider.example. 2024031701 3600 600 1209600 3600
#                                     ^ SOA serial 号变化能反映 DNS 变更次数

# TTL 变化会暴露"曾做过切换"
dig digiforensics-site +noall +answer

取证要点:SOA 的 serial 号(如 2024031701)是管理员手动递增的,每一次 DNS 记录变更都会让它变大。

serial 异常大意味着历史上做过大量变更——包括可能的解析切换。

配置错误导致的源站暴露(最常见的泄露途径)

错误类型 表现 检查方法
子域未接入 CDN origin. direct. test. dev. 直连源站 CT log + 子域枚举
邮件记录 DNS MX 记录通常必须指向真实服务器 dig MX digiforensics-site
非标准端口服务 源站 8888/8080 等端口未做防护 端口核对(限委托范围)
源站 IP 上部署的其他域名 同 IP 承载多站 CT log + 证书反查
错误页泄露 源站 4xx/5xx 页面暴露了 upstream 地址 直连源站观察(授权范围内)
**源码与配置泄
安全验证 当前请求需要先完成一次滑块验证。