F5 Shape SDK逆向分析:从采集机制到误杀排查全解
2026/9/16 23:35:01 网站建设 项目流程

搞过风控对抗或者Bot防护的人,估计都见过这场景:某天早上站点忽然涌进大量客服投诉,说正常用户总是被弹验证、登录死活过不去。问题查来查去,最后定位到页面里多了一段来自shape.securityf5-dfcs.com的脚本。F5 Shape这套东西在美西南这类航旅站点、以及我那代号xbk的测试环境里,出现的频率是真的高。标题里说的“逆向分析”,不是要绕过它去搞什么灰产,而是作为安全研究员或者甲方风控,必须搞清楚它到底采集了什么、决策链路长什么样、误杀是怎么来的。这篇文章就是我近期对F5 Shape最新版SDK做机制拆解的完整记录,从抓包到解混淆,再到美西南和xbk两种场景下的信号差异,最后落到防御协作和误杀排查。

1. 为什么会盯上F5 Shape:一次“被误封”的排查现场

1.1 误封现象与第一反应

凌晨三点,告警群突然响起来,接口成功率从99.9%掉到70%出头。第一反应是回源出问题了,或者是CDN的某个节点抽风。但打开流量看,发现大量来自住宅宽带的正常会话都拿到了一个challenge响应,紧接着页面就被插入了一段图灵测试。这种“大规模误杀”的典型特征,基本就在提示我们:机器人防护系统的策略被某个信号击穿了,导致正常用户被连带拦截。

排查初期的常见错误是盯着应用日志找异常,但应用层根本没有错误——请求都正常到达了后端,只是在更前置的Bot防御层被扣下了。再到响应头一看,Set-Cookie里出现了__shape_a__shape_b这一串东西,才确认是F5 Shape在起作用。平时它是隐形防守者,一旦误杀,就成了最大的背锅侠。

1.2 Shape不是单点,而是一条完整决策链

很多刚从应用防护转过来的人,会把F5 Shape理解成一个“JS脚本”,以为去掉脚本就绕过了。这是最大的误区。我前前后后分析过几次,Shape这套东西拆开来看至少四层:

  1. 采集层:下发到浏览器的SDK负责采集设备指纹、行为轨迹、DOM环境等信息。
  2. 保护层:SDK自带代码混淆、反调试、反模拟机制,防止被人直接抄作业。
  3. 决策层:采集到的数据上传到F5的云端模型,服务端结合IP信誉、历史行为、会话风险等给出 allow / challenge / block 的结论。
  4. 执行层:结论通过Set-Cookie、页面注入、API响应头传回浏览器,决定是放行、弹验证还是拒绝。

这套结构的核心在于:前端SDK只是收集端,真正的判定逻辑在云端。只盯着JS看,等于只拿到了整条链路的20%。很多做逆向分享的人喜欢把SDK里的字符串解出来贴一堆,看起来很厉害,但离“搞懂这套东西”还有很长的距离。

2. Shape系最新版SDK的可观察特征:从入口脚本到下发链路

2.1 SDK入口的加载方式与识别要点

被Shape保护的站点,入口脚本不一定叫shape.js,新旧版本差异不小。老版本常见的是直接加载https://*.shape.security/script/v1/...,而新版本统一走F5分布式云体系,域名经常是*.f5-dfcs.com,有时还会套一层CloudFront这类CDN做分发,脚本名有时还带随机化参数。单凭脚本文件名判断版本,很容易翻车。

观察入口要从三个层面入手:

  • 加载方式:同步加载还是异步加载、是否带defer、是否在DOMContentLoaded之后才动态插入。
  • 版本探测:SDK初始化时会向服务端发一个配置请求,响应里通常包含当前下发版本号、启用的检测模块列表、灰度开关等。这是判断“最新版”最直接的途径。
  • 脚本内容特征:主脚本里会有分片加载逻辑,新版手感和旧版完全不一样。

提示:新版SDK的版本控制是灰度制的。美西南这种大流量站点,同一时间线上可能同时在跑两三个不同版本,不同地区、不同会话拿到的脚本也不一样。分析时如果只缓存了一份样本,得出的结论可能就是错的。

2.2 新版与老版的核心差异

结合我这段时间对美西南站点和xbk环境的观察,最新版相对上一代,至少有四个明显变化:

第一,脚本从单文件变成了分片加载。老版一个shape.js从头到尾,新版的模块被拆成多个chunk,运行时按需拉取,哪些模块被加载取决于页面类型和环境特征。这在分析时要多看好几个请求,工作量直接翻倍。

第二,遥测接口的路径变成了动态签名。老版上报数据的URL相对固定,可以比较容易地抓到完整数据包。新版上报路径带一次性签名参数,并且reportconfigchallenge走不同的端点,光靠抓包把所有请求拼齐就需要不少耐心。

第三,检测点大量挪进了Web Worker。新版把一部分采集逻辑放到了独立的Worker线程里跑,主线程代码被混淆后基本看不出采集逻辑。调试时如果不开Worker的线程面板,很容易漏掉关键上报点。

第四,关键数据不再直接写进Cookie。老版本里有一段时间,部分特征会被编码塞进Cookie,存在本地。新版Cookie里存的多半是临时票据或一次性token,真正关键的状态全部服务端掌握。这意味着想靠纯前端分析伪造出一个“合法Cookie”来直接过关,基本是走不通的,新版的判定核心已经彻底云端化了。

2.3 服务端决策的响应形态

SDK跑完采集,服务端的结果最终要传递到浏览器和业务方,常见的有几种形式:

响应形态常见标记典型含义变更频率
Set-Cookie__shape_a短期会话标记,生命周期短每次会话/请求级刷新
Set-Cookie__shape_b长期设备标记,生命周期长相对稳定
Set-Cookie__shape_shape验证状态相关,出现它说明策略介入触发验证/解除验证时变化
响应头shape-render-mode指示前端以何种方式渲染验证策略变更时变化
页面注入动态插入的challenge JS触发人机验证界面命中策略时出现

排障的时候,关键在于分清“它到底只是给你打了标记,还是已经发了验证指令”。前者往往只是常规采集,后者才是用户被卡住的直接原因。如果发现__shape_a__shape_shape同时出现且策略ID在变化,多半是会话风险分在动态调整。

3. 实际逆向分析流程:抓包、断点、AST解混淆、行为验证

3.1 环境与工具链选型

做这类分析,环境干净比技术本身更重要。我常用的组合是:

  • 抓包:Charles或mitmproxy,用于看完整请求链路,重点记录SDK的配置请求、上报请求、Cookie变化顺序。
  • 浏览器调试:Chrome DevTools,配合断点看JS运行时的调用栈。
  • 代码美化与解混淆js-beautify负责格式化,Babel或ast-grep负责AST级别的结构还原。
  • 行为对照:Puppeteer/Playwright,用来造不同指纹、不同行为模式的浏览器环境做对比实验。

注意:整个分析过程必须限定在已授权范围内,比如自建测试环境、与防护厂家的合作项目,或者你对美西南这类第三方站点做的是“仅观察不干扰”的被动分析。任何插件注入、流量修改、验证尝试都必须有授权依据,这是基本底线。

3.2 定位关键函数的完整链路

实际动手时,我会按下面的顺序把链路完整走一遍,这也是我觉得最有价值的一套方法论,而不是某个特定版本的特征:

第一步:找到SDK入口,保存脚本。在DevTools里过滤shapef5,定位到主脚本URL,保存后先跑js-beautify格式化,把压缩成一行的大文件还原成可读结构。

第二步:特征字符串地毯式搜索。打开格式化后的代码,搜canvasgetBatterymousetouchstartsendBeaconXMLHttpRequestlocalStorageindexedDB这些关键词。新版混淆比较狠,字符串经常是动态拼的,但关键API名总会出现在某个不太起眼的位置。找不到就说明做了字符串数组偏移,得用AST还原。

第三步:在Cookie写入点和网络请求处下断点。打开Sources面板,在Document.cookie的setter、sendBeaconXMLHttpRequest.sendfetch上打断点。刷新页面,观察SDK在哪个执行阶段触发了网络请求,请求体里带了什么参数。这样能把“采集代码”和“上报时机”对应起来。

第四步:顺着调用栈找到数据封装函数。从断点往上翻调用栈,找出发送前的数据序列化逻辑,那里通常是特征打包的地方。新版会用动态属性名做一层包装,AST解混淆后字段名还是可能不直观,这时候别死磕,用“黑盒观测+关键节点断点”配合,把字段名和实际值对应起来记录一版就够了。

第五步:对照测试验证权重。这一步最关键。做一个最小环境,用不同指纹和行为组合去访问同一个接口,记录每次的决策结果,反推哪些信号权重高。

3.3 解混淆之后看什么:采集维度清单

地址解完、函数理清以后,重点看SDK到底从哪些维度采集数据。我整理了一个常观察的维度表:

维度类别具体采集点潜在用途
基础环境UA、Accept、Accept-Language、时区、语言建立基础画像
硬件指纹canvas、WebGL、字体、屏幕分辨率、设备内存、CPU核数识别真实设备
存储痕迹localStorage、IndexedDB、Cookie可用性探测判断是否清白历史
行为轨迹鼠标、触摸、键盘事件间隔、滚动行为判断“人来操作”还是脚本操作
网络特征RTT小包探测、连接类型、Service Worker状态估算网络环境与代理特征
环境异常iframe嵌套、窗口尺寸、WebDriver标记、DevTools检测检测浏览器是否被自动化控制

这些维度单个看都弱,“UA+时区+屏幕”就能撞出一大片人,但组合起来、再叠加服务端的动态模型,区分度就上来了。这也是为什么Shape敢把判定全部放云端——因为前端采集的数据本身是“原材料”,模型才是“加工厂”。

3.4 一个最小观测脚本的思路

下面这个脚本,目的是复现“把SDK的运行过程录下来”这个动作,方便对比不同环境下的行为差异。它不伪造任何东西,只是观察:

// 观测性示例,仅用于已授权环境的SDK行为分析 const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch({ headless: false, // 用有头模式,尽量接近真实环境 args: ['--window-size=1366,768'] }); const page = await browser.newPage(); // 记录SDK相关的请求和响应细节 page.on('response', async (resp) => { const url = resp.url(); if (url.includes('shape') || url.includes('f5')) { console.log('[SDK request]', resp.status(), url); } }); // 打开目标URL(请替换成你具备分析权限的站点/测试环境) await page.goto('https://your-lab-site.com', { waitUntil: 'networkidle2', timeout: 60000 }); // 输出当前页面上的shape相关cookie const cookies = await page.cookies(); console.log(cookies.filter(c => c.name.startsWith('__shape'))); await browser.close(); })();

跑通之后,多准备几组对照环境:

对照组环境情况常见结果
A组有头Chrome,正常鼠标移动,停留一段时间再操作基本不触发验证
B组无头模式、直接发请求、零交互高概率触发challenge
C组有头Chromium但自动化标记明显大概率被标记为可疑

对照实验的目的,不是写一个“逃过检测”的脚本(那条路既没意思也没授权),而是搞清楚哪些信号权重大、哪些行为最容易触发误杀——这恰恰是甲方排障时最需要的信息。

4. 美西南和xbk两种场景下的检测信号差异

4.1 站点形态差异对SDK的影响

美西南(Southwest Airlines)这类航旅站点的体量摆在那,子域多、业务链路长,登录、搜索、支付、会员中心各自都有独立页面。实际观察下来,不同子域加载的SDK配置经常不是一套:有的页面只加载基础采集版,有的页面会额外加载增强验证模块。加上流量大,A/B测试和灰度发版几乎全天在线,同一个用户上午下午拿到的SDK都可能是不同版本。

相比之下,xbk是我自己搭的测试站点代号,用的是同一家SDK服务,但流量小、业务路径短、也没有灰度策略,SDK版本相对固定。这带来的一个实际影响是:在xbk上分析出的结论不能完全等价套到美西南场景,版本差异、策略差异、流量模型差异都可能导致行为信号不一致。

4.2 行为阈值会随风险状态动态变化

这是我在两个场景里对比之后印象最深的点:同样是“没有鼠标移动、快速连续请求”的行为模式,在xbk环境可能只是分数上调,在美西南环境很可能直接触发验证。这说明服务端不是用的固定规则,而是接了实时风险变量——IP信誉、会话新鲜度、请求频率、路径异常度都会影响最终阈值。

换句话说,同一套采集数据,在不同时间、不同入口、不同风险上下文中,对应的决策可能完全不同。做逆向分析时如果只拿一次结果下结论,很容易被误导。正确打开方式是拉长观测周期,记录至少一周的决策结果变化,再去找规律。

4.3 容易被忽略的网络侧信号

除了浏览器端的行为数据,Shape服务端还会结合网络侧信息做交叉验证。比较常见的是看到IDC出口IP即使浏览器指纹完全正常,也容易进验证队列;而住宅宽带、移动网络的用户即使指纹有一点点偏差,分也不一定高。这个“网络画像+设备指纹”的交叉逻辑,是很多误杀的来源。

对甲方运维来说,这个特性的直接启示是:排查误杀不能只让用户“换个浏览器试试”,还要看他的出口IP类型和会话建立方式。来自云厂商出口、数据中心出口的办公网用户,被Bot防护误伤的概率天然高一些,这不是前端能解决的问题,只能通过策略白名单或调整阈值来缓解。

5. 搞懂机制之后,真正能落地的事:防御协作与误杀排查

5.1 一套完整的误杀排查链路

如果你是被Shape保护的站点方,最常见的需求不是“分析SDK怎么工作”,而是“现在用户被误杀了,怎么快速定位”。我踩过几次坑之后,总结了这条链路:

  1. 收集现场信息:拿到目标用户的UA、IP、访问时间、会话Cookie,先确认同一IP段、同一UA模式的用户是否大面积受影响。
  2. 区分决策类型:看响应是challenge(弹验证)还是block(直接拒绝),两种处置对应的排查方向完全不同。
  3. 确认SDK版本:让用户清缓存重新加载页面,看下发的是哪个版本。如果新版本刚灰度不久,误杀概率会显著上升。
  4. 简单A/B确认:在测试环境临时屏蔽SDK,模拟相同操作路径,看问题是否消失。如果消失了,就基本锁定是Bot层的问题,要反馈给防护团队。
  5. 提交加白或调参工单:把上面收集的信息整理成证据链,联系F5或厂商侧调整策略、加白特定UA/路径,或者把风险阈值从严格调回中等。

5.2 与风控、研发协作时的日志字段清单

误杀排查最怕部门墙,风控说“是网络问题”,研发说“应用层没报错”,安全说“是防护策略误杀”。要打破这个局面,关键是大家手里有一份统一的排障日志。建议至少要记录这些字段:

字段说明
时间戳精确到秒,保留时区
会话ID / 用户标识用于关联前后端日志
Shape决策结果allow / challenge / block
触发策略ID方便定位是哪些规则生效
客户IP(脱敏)判断是住宅、移动还是IDC出口
UA判断是正常浏览器还是自动化工具
SDK版本号定位是否灰度新版本引入
验证页面URL用户到底是被哪个页面的验证卡住

这份清单的意义在于,三方都能基于同一组数据说话,而不是互相猜。拿到工单的第一时间,先看“决策结果”和“触发策略ID”,比看一百行应用日志都管用。

5.3 几个值得长期观测的健康指标

最后建议被防护方把下面几个指标做成日常监控。它们不是单纯看“有没有被攻击”,而是看“防护系统有没有误伤良民”,两者同等重要:

  • challenge率:被弹验证的请求占总请求的比例,正常波动应该在某个范围,突然飙升就是要出事了。
  • 验证通过率:弹了验证之后有多少人能真正通过,过低说明验证门槛太高或策略误杀严重。
  • 接口成功率:新增或变更防护策略后,这个指标是最直观的“用户体验体检表”。
  • SDK加载耗时:Shape SDK不算轻量,对首屏渲染有可感知的影响,建议设一个性能预算。
  • 版本号分布:保持对线上SDK版本的感知,灰度出了问题可以在几小时内止损。

这套指标配上前面的排查链路,基本能覆盖日常防护运维里90%的误杀场景。

我个人在实际项目里的体会是:F5 Shape这类云端Bot防护的逆向分析,价值不在于“破解一个版本”,而在于建立一套长期有效的“版本追踪+行为观察+误杀排查”机制。今天分析的结果,三个月后很可能就因为一次灰度更新全部作废,但方法论和分析链路是可以一直复用的。所以我会把每次观察到的版本特征、决策变化、踩坑记录归档成一个持续更新的特征库文档,排障时直接查,效率比临时翻混淆代码高得多。另外说一句,很多讨论会把F5 Shape和F5 BIG-IP、F5 NGINX的漏洞通知混在一起,实际上Shape是收购来的独立产品线,走的是SaaS化Bot防御订阅,和自运维的BIG-IP/Nginx基础设施是两个体系,排障时不要混着看,不然会白白浪费很多时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询