首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >反爬技术实战:从请求链路到行为模型的纵深防御

反爬技术实战:从请求链路到行为模型的纵深防御

原创
作者头像
用户12818508
发布于 2026-10-20 14:05:21
发布于 2026-10-20 14:05:21
630
举报

上一篇从爬虫视角讲了如何绕过防护,这篇换到防守方。2026 年的反爬已经不再是“封 IP + 弹验证码”的简单组合。Cloudflare 一家就保护着超过 2400 万个活跃站点,2025 年 7 月起在全网默认拦截 AI 爬虫。Wappalyzer 追踪的网络安全服务数量在 2022 到 2024 年间从 36 个翻倍到 60 个。防守方手里的牌多了,但成本也在涨——关键不是把所有手段堆上去,而是搞清楚每一层的拦截收益和代价。

以下按请求到达的先后顺序,逐层拆解可落地的反爬策略,附配置或代码示例。


第一层:在握手阶段就拒绝——TLS 与 HTTP/2 指纹

这是当前性价比最高的拦截层。攻击者可以换 IP、改 UA、清 Cookie,但 Python requests 库的 TLS ClientHello 包与真实 Chrome 的差异是结构性的。requests 使用 OpenSSL,其加密套件顺序、扩展列表、椭圆曲线参数与 Chrome 的 BoringSSL 栈完全不同。风控系统在 HTTP 请求体被解析之前,就能从 TLS 握手判断出“这不是浏览器”。

JA3 与 JA4 的区别值得注意。JA3 是 2017 年 Salesforce 提出的方案,对 ClientHello 中五个字段做 MD5。但 Chrome 从 2023 年开始随机化 TLS 扩展顺序,导致同一版本的 Chrome 每次连接的 JA3 都不同,JA3 的稳定性被破坏。JA4 是 FoxIO 2023 年发布的替代方案,核心改进是排序后再哈希,因此不受扩展顺序随机化的影响,同时格式可读性更强。一个典型的 JA4 长这样:t13d1516h2_8daaf6152771_02713d6af862,其中 t13 表示 TLS 1.3 over TCP,d 表示有 SNI,15 和 16 分别是加密套件和扩展的数量,h2 表示 ALPN 协商到了 HTTP/2。

防守方落地方式:在 Nginx 或边缘网关上集成 JA3/JA4 计算模块。如果接入 Cloudflare,其默认已启用该检测。自建方案可参考开源实现,在 TLS 握手完成后立即计算指纹,与已知浏览器指纹库比对。不匹配的请求直接返回 403,无需进入应用层。

但这一层有一个容易被忽略的盲区:HTTP/2 指纹同样在泄露身份。Chrome 的 HTTP/2 连接有特定的 SETTINGS 参数、窗口大小、伪头顺序和优先级帧序列。Akamai 的 HTTP/2 指纹哈希是事实标准,主流 CDN 都会校验。一个用 requests 发起、声称自己是 Chrome 的请求,TLS 指纹不对,HTTP/2 的 INITIAL_WINDOW_SIZE 也不对——两处不一致叠加,拦截置信度直接拉满。

成本评估:低。边缘层完成,不消耗源站资源。适合所有规模的站点。


第二层:让脚本“看不懂”页面——DOM 随机化与数据混淆

通过了指纹检测的爬虫,下一个目标是解析页面结构。如果每次加载的 DOM 都不同,基于固定 CSS 选择器的解析逻辑会立即失效。

动态类名是成本最低的实现。服务端在渲染时生成随机后缀,注入到关键元素的 class 中:

代码语言:javascript
复制
# 服务端渲染时的简单实现(以 Jinja2 为例)
import random, string

def random_class(prefix="c"):
    suffix = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))
    return f"{prefix}_{suffix}"

# 模板中
<div class="{{ random_class('price') }}">{{ product.price }}</div>
# 渲染结果可能是:<div class="price_x7k2m9qp">¥299</div>

攻击者仍然可以通过“找包含价格文本的 div”来定位,但纯结构化的批量提取需要更多启发式逻辑,成本上升。

更进一步的方案是字体映射加密,将数字转为自定义 Unicode 码位,前端用自定义字体渲染。用户在浏览器中看到的是 “¥299”,但 DOM 里是 &#xE4A2;&#xE4B7;&#xE4C3; 这类码位。爬虫即使抓到了文本,也无法还原数值,除非逆向字体文件并建立码位映射表。雪球、财联社等金融信息站点有类似实践。

Canvas 渲染关键数据则更彻底。价格、手机号等敏感字段不输出为文本节点,而是绘制到 <canvas> 上。爬虫需要调用 OCR 或模拟 Canvas 渲染才能提取,技术门槛和计算成本都显著抬高。

代价:服务端渲染复杂度上升,前端调试变困难,对无障碍访问(屏幕阅读器)不友好。适合数据价值高、解析难度需要刻意拉高的场景,不适合内容型站点的大规模应用。


第三层:行为评分——从“是不是爬虫”到“像不像人”

规则拦截(UA 黑名单、固定频率封禁)对稍有经验的攻击者已基本无效。当前主流方案是对每个会话实时打分,而不是做二值判断。

Cloudflare 对每个请求计算 bot score(0–100),低于阈值直接拦截或挑战。其模型基于每天数百亿请求训练。DataDome 每天处理约 3 万亿条信号,侧重设备指纹与交互行为。这些系统的共同点是:不只看单个请求,而是看会话的时序模式。

一个真人会话的典型特征包括:请求间隔呈对数正态分布(偶尔很长,多数较短),会在某些页面停留远超加载时间,会加载静态资源(CSS、图片、favicon),偶尔回退或打开新标签页。而一个追求吞吐的爬虫,往往只请求数据端点、跳过静态资源、间隔精确或接近均匀分布。

自建轻量级行为评分的可行路径:不需要一开始就上 LSTM。用 LightGBM 加规则引擎,特征可以很简单:

特征

真人典型值

爬虫典型值

请求间隔标准差

较高(>1s)

极低(<0.1s)

静态资源请求占比

30–60%

接近 0

页面停留时间分布

长尾

集中在极短区间

会话内翻页速度

不均匀

均匀或递增

是否触发鼠标/滚动事件

有

无或规则化

在 Nginx 层采集这些信号(需要配合前端 JS 上报鼠标和滚动事件),送入轻量模型实时评分。超过阈值触发验证码或限速,而不是直接封禁——直接封禁的误伤成本高,且会给攻击者明确的反饋信号。

成本:中等。需要前端埋点、日志管道和模型维护。适合有专职安全或风控团队的中大型站点。


第四层:蜜罐与时间陷阱——让爬虫自己暴露

蜜罐的核心逻辑是:人类不会看到的东西,爬虫会碰到。

最经典的实现是在页面中插入一个对人类不可见(display: none 或 opacity: 0)的链接或表单字段。正常用户不会点击或填写,但一个盲目遍历 DOM 所有 <a> 标签或自动填充所有 input 的爬虫会触发它。一旦触发,该 IP 或会话立即进入观察名单或直接封禁。

Cloudflare 在 2025 年推出的 AI Labyrinth 把蜜罐做成了规模化产品。它在受保护站点中注入大量 nofollow 的隐形链接,这些链接指向由 AI 生成的、看起来像真实内容的虚假页面。遵守 nofollow 的爬虫(多数有声誉顾虑的爬虫会遵守)不会进入;不遵守的爬虫则陷入“无尽链接迷宫”,消耗计算资源在虚假内容上,同时其指纹被记录并共享给所有启用该功能的 Cloudflare 客户。

时间陷阱(tarpit) 是蜜罐的变体:对疑似爬虫的请求返回极慢的响应(如每 30 秒返回一个字节),把攻击者的并发连接数占满,同时不消耗源站的实际计算资源。OpenBSD 的 spamd 是这一思路的早期实现,现代边缘计算平台(Cloudflare Workers、Fastly Compute)可以低成本复刻。

注意:蜜罐的字段和链接需要定期更换,否则爬虫作者会在解析逻辑中学会跳过它们。这是一场持续的小幅对抗。


第五层:动态令牌与请求链路校验

如果攻击者使用无头浏览器或高度模拟的客户端绕过了前述所有层,最后一道防线是业务逻辑本身。

动态令牌的常见形态是:关键接口(如详情页、列表翻页)需要携带一个由前端 JavaScript 在页面加载后计算的签名。签名通常由时间戳、随机数、页面中某些动态值(如前面提到的随机类名)拼接后经混淆算法生成。后端用相同逻辑验证。攻击者需要逆向 JavaScript 混淆代码才能复现签名生成过程。

代码语言:javascript
复制
// 前端签名生成示例(简化)
function genSign() {
  const ts = Date.now();
  const nonce = Math.random().toString(36).slice(2);
  const pageToken = document.querySelector('[https://c.njjiamei.com]').dataset.token;
  const raw = `${ts}:${nonce}:${pageToken}:secret_salt`;
  return { ts, nonce, sign: md5(raw) };
}

请求链路连贯性校验是更轻量的替代方案:校验用户是否按合理顺序访问。一个正常用户访问商品详情页之前,通常经过了列表页或搜索页;而一个直接遍历 /product/12345 的爬虫,Referer 为空或来源异常。强制关键页面依赖前置页面的 Cookie 或 Referer 即可拦截大量简单遍历。

代价:前端代码复杂度上升,签名逻辑一旦泄露需要整体更换。适合核心数据接口,不适合全站铺开。


成本与收益的取舍

把上面五层全部部署,拦截率会很高,但运维成本和误伤风险也会同步上升。一个务实的落地顺序是:

  1. 先上边缘层:TLS/HTTP2 指纹拦截 + IP 限流。成本最低,拦截效果立竿见影,对正常用户零感知。
  2. 再上行为评分:从简单的规则开始(静态资源占比、请求间隔方差),逐步迭代。不要一开始就追求复杂模型。
  3. 按数据价值决定是否加密 DOM:只有高价值、易被批量抓取的数据字段才值得做字体映射或 Canvas 渲染。
  4. 蜜罐和动态令牌作为补充:部署成本不高,但需要定期维护更新。

最后回到一个根本问题:反爬的收益边界在哪里。如果攻击者愿意为你的数据投入持续逆向和代理成本,没有任何技术方案能永久阻止。反爬的目标不是“绝对阻止”,而是把攻击成本推到高于数据价值,或者推到攻击者愿意转而寻找更容易的目标。同时,任何防护措施都不应以牺牲正常用户体验或可访问性为代价——一个需要真人用户做三道滑块验证才能看价格的网站,在赶走爬虫之前可能先赶走了客户。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 第一层:在握手阶段就拒绝——TLS 与 HTTP/2 指纹
  • 第二层:让脚本“看不懂”页面——DOM 随机化与数据混淆
  • 第三层:行为评分——从“是不是爬虫”到“像不像人”
  • 第四层:蜜罐与时间陷阱——让爬虫自己暴露
  • 第五层:动态令牌与请求链路校验
  • 成本与收益的取舍
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档