找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1628

积分

0

好友

212

主题
发表于 9 小时前 | 查看: 2| 回复: 0

论文《The WASM Cloak》封面,标题为《The WASM Cloak: Evaluating Browser Fingerprinting Defenses Under WebAssembly based Obfuscation》

论文评分:8.5/10

这篇来自德克萨斯大学圣安东尼奥分校和Google合作的研究,于2025年8月发表在arXiv预印本平台上(推测是投向某个安全顶会)。它探讨了一个核心问题:当攻击者将JavaScript指纹脚本转换成WebAssembly格式进行混淆后,现有的指纹检测工具还能有效工作吗?

答案是:学术界的检测器受到了显著冲击——DeepFPD的召回率(recall)从78%跌至44%,而FP-Inspector则因为其依赖的旧版浏览器(Firefox 52 ESR)根本不支持WASM而完全失效。然而,商业工具(如CanvasBlocker、Privacy Badger、Firefox的严格模式)却毫发无损,因为它们是在API层面进行拦截的,根本不在乎底层代码是JavaScript还是WebAssembly

一、这篇论文在解决什么问题?

这项联合研究用一个问题概括了其核心目标:当攻击者把JavaScript指纹脚本转换成WebAssembly混淆后,现有的指纹检测工具还能检测到吗?

这个问题的背景在于,现有的浏览器指纹检测工具(无论是学术界的DeepFPD、FP-Inspector,还是商业界的CanvasBlocker、Privacy Badger)几乎都是为JavaScript设计的。它们要么分析JS的抽象语法树(AST)、控制流图(CFG)、词元序列,要么在运行时监控JS API的调用。但WebAssembly是一种低级的二进制格式,缺乏高级的语义结构,对其进行静态分析极其困难。

更关键的是,已有研究证明WASM可被用于恶意代码混淆。2022年Romano等人的“Wobfuscator”论文展示了如何将JS恶意软件选择性地转换成WASM来逃避检测。到2025年,Guri等人甚至提出了纯WASM实现的指纹技术,通过WASM和JS之间的执行时间差异来区分浏览器。

然而,在此之前,还没有人系统地评估过:如果把现实世界中的JS指纹脚本都转换成WASM,现有的检测工具会表现如何?

  • 学术检测器(DeepFPD、FP-Inspector):遭遇重挫。DeepFPD在其自身测试集上的召回率从77.78%跌至44.44%;FP-Inspector则因为依赖不支持WASM的Firefox 52 ESR而完全失效。
  • 商业工具(CanvasBlocker、Privacy Badger、Firefox Strict Mode、Brave):完全不受影响。因为它们在API层进行拦截(伪造或阻止),无论底层代码是JS还是WASM。

这个结果揭示了一个深刻的现状:学术界与工业界在指纹防御上走了两条截然不同的路径,而从实际效果看,工业界的路径更为有效。

二、JS-to-WASM转换管道拆解

论文的核心技术贡献是一个自动化的JS-to-WASM转换管道,该管道定义了13条转换规则。

设计哲学:贪婪转换 vs 选择性转换

论文采用了贪婪转换策略:尽可能多地将JS代码转换成WASM,而不是只转换某些关键函数。

原因很直接:大多数检测工具是黑盒,你无法知晓它们依赖哪些特征。如果只转换一小部分代码,可能恰好避开了检测器关注的部分。贪婪转换则能最大化“破坏检测器特征”的概率。

这一策略与Wobfuscator的“机会主义转换”不同。Wobfuscator是选择性的——它只转换那些“转换后能逃避检测”的函数。但Wobfuscator针对的是恶意软件检测器,可以通过实验找出关键函数。指纹检测器的特征空间更复杂,贪婪转换是更保险的选择。

13条转换规则

规则分为两类:9条通用规则(针对语言结构)和 4条指纹专用规则(针对指纹相关的API调用)。

通用规则(Rule 1-9):

  1. 替换字面量:把 let x = 42 转换成WASM导出的常量,JS通过 instance.exports.x_pos 访问。字符串字面量则存放在WASM的线性内存中,通过 getString 辅助函数重建。
  2. 混淆敏感函数调用:把 eval(code)Function(code)atob(data) 等动态执行函数的名字存放在WASM内存中,运行时通过 window[getString(pointer)](arg) 间接调用。这样静态分析就看不到 eval 这个字符串。
  3. 替换数组:把整数/浮点数组的数据存放在WASM线性内存中,JS通过 Int32Array(instance.exports.memory.buffer, ptr, length) 访问。
  4. 替换if-else:把条件分支的then和else块作为import对象传给WASM,WASM根据条件标志(0或1)调用对应的JS函数。
  5. 替换for循环:把循环体作为import对象,WASM用while循环实现for的语义。
  6. 替换while循环:类似for循环,条件判断和循环体都作为import对象。
  7. 替换无返回值的函数调用:把 doSomething() 包装成WASM导出函数,JS调用 instance.exports.f_pos()
  8. 替换类定义:把整个class定义作为字符串存在WASM中,运行时通过 document.createElement('script') 动态注入DOM。
  9. 替换函数定义(LLM驱动):这是最复杂的一条规则。使用Qwen2.5-Coder-14B(一个140亿参数的代码生成LLM)将JS函数翻译成AssemblyScript(一种可编译为WASM的类TypeScript语言)。提示词很简单:“Write the following JS function in AssemblyScript, name it func_def_123, and export it. Only provide the code; no explanation or use case.”

指纹专用规则(Rule 10-13):

  1. 混淆指纹相关的成员表达式:把 canvas.toDataURLnavigator.hardwareConcurrencyscreen.availHeight 等API调用的属性名拆成两半(如“fill”和“Text”),分别存在WASM中,运行时拼接。这样静态分析看不到完整的API名。
  2. 混淆动态代码生成:把 canvas()screen.availHeight 这样的表达式作为字符串存在WASM中,运行时通过 window[getString(e_call)][getString(c_str)] 间接执行。
  3. 正则混淆“canvas”字符串:用正则表达式 ((['\"]|\\)+canvas(['\"]|\\)+) 找到所有包含“canvas”的字符串字面量,拆成“can”和“vas”两部分存在WASM中。
  4. 混淆十六进制编码的属性访问:检测像 "\x61\x76\x61\x69\x6C\x48\x65\x69\x67\x68\x74"(十六进制编码的“availHeight”)这样的混淆模式,并进一步拆分成WASM字符串。

技术亮点

LLM驱动的函数翻译(Rule 9):这是论文中最有意思的设计。JS函数的语法和语义极其多样——闭包、高阶函数、动态类型、异步回调——用确定性规则处理这些变化几乎是不可能的。LLM提供了一个灵活的“通用翻译器”。

但效果并不理想:Rule 9的转换成功率只有9.70%,是所有规则中最低的。转换时间也最长(3.40秒/脚本)。论文没有深入分析失败原因,但可以推测:LLM生成的AssemblyScript代码可能无法编译(类型错误、语法错误),或者编译后的WASM在运行时的行为与原始JS不一致。

单一WASM模块设计:所有转换后的代码都被编译进同一个WASM模块,而不是每条规则生成一个独立模块。这样可以减少编译和实例化的开销,避免多个异步 WebAssembly.instantiate() 调用带来的嵌套复杂性。

异步实例化:使用 WebAssembly.instantiate() 而不是同步的 WebAssembly.Instance(),遵循MDN的最佳实践。转换后的代码被包裹在一个IIAFE(立即调用的异步函数表达式)中,确保WASM模块在使用前已完成实例化。

性能数据

在750万个JS脚本(其中包含10,742个指纹脚本)上进行测试:

  • 转换成功率:85.76%(95%置信区间很窄,表明结果稳定)。
  • 代码覆盖率:25.01%(平均有25%的原始JS代码被转换成了WASM)。
  • 转换时间:3.45秒/脚本。
  • 代码膨胀:24.54%(转换后的JS + WASM比原始JS大了24.54%)。

按指纹类别细分:

  • Canvas-font:100%成功率,但覆盖率只有0.01%(这些脚本可能非常简单)。
  • Canvas:74.59%成功率,覆盖率8.65%(最难转换的类别)。
  • WebRTC:78.93%成功率,覆盖率40.31%(覆盖率最高)。
  • AudioContext:92.92%成功率,覆盖率18.66%。

三、对学术检测器的冲击

DeepFPD:召回率腰斩

DeepFPD是2024年发表的一个深度学习指纹检测器,使用LSTM和GGNN来分析AST、词元序列和CFG。论文作者在复现时发现其深度学习管道依赖过时的库而无法运行,因此改用DeepFPD的特征提取器 + 随机森林分类器(论文作者自己也提到随机森林效果更好)。

在DeepFPD自己的测试集上(311个非指纹 + 18个指纹脚本):

  • JS版本:准确率 97.18%,精确率 73.68%,召回率 77.78%。
  • WASM版本:准确率 95.92%,精确率 72.73%,召回率 44.44%

召回率从78%跌至44%,几乎腰斩。这意味着超过一半的指纹脚本在转换成WASM后逃过了检测。

在论文新采集的真实数据集上(500个脚本,400个指纹 + 100个非指纹):

  • JS版本:准确率 91.52%,精确率 99.42%,召回率 86.40%。
  • WASM版本:准确率 88.54%,精确率 99.70%,召回率 81.28%

召回率只下降了约5个百分点,影响小得多。

为什么两个数据集的结果差异如此之大?

论文进行了特征重要性分析。DeepFPD最重要的10个特征是:

  1. MemberExpression:screen (0.0214)
  2. MemberExpression:fillText (0.0192)
  3. CallExpression:canvas (0.0186)
  4. MemberExpression:language (0.0145)
  5. MemberExpression:localStorage (0.0118)
  6. MemberExpression:appName (0.0116)
  7. MemberExpression:platform (0.0110)
  8. MemberExpression:fillStyle (0.0106)
  9. MemberExpression:colorDepth (0.0105)
  10. MemberExpression:fillRect (0.0089)

论文绘制了delta图,显示WASM转换后每个特征的贡献变化。结果发现:

  • MemberExpression:screen 的贡献下降最多。
  • MemberExpression:getTimezoneOffsetBinaryExpression:typeProperty:getScreenResolution 等特征也大幅下降。

这些特征在DeepFPD的测试集中占比较高,因此WASM转换对它们的抑制直接导致了召回率暴跌。而在论文的真实数据集中,这些特征占比较低,更多的是Canvas渲染相关特征(canvasfontfillRectplugins),这些特征受WASM转换的影响较小,所以召回率下降有限。

重新训练能恢复性能吗?

论文尝试使用WASM转换后的脚本重新训练DeepFPD。直接将WASM脚本加入训练集会令模型崩溃(准确率和精确率跌至5-6%,召回率保持100%——模型把所有样本都标记为指纹)。

但如果进行平衡增强(414个Canvas + 537个AudioContext + 190个WebRTC指纹脚本 + 525个非指纹脚本),模型恢复了:

  • JS测试集:准确率 98.12%,精确率 87.50%,召回率 77.78%。
  • WASM测试集:准确率 98.12%,精确率 87.50%,召回率 77.78%。

性能完全一致。这说明DeepFPD并非不能检测WASM,而是需要用WASM数据来训练。但这暴露了一个根本问题:基于代码特征的检测器需要持续更新训练数据,否则就会被新的混淆技术绕过。

FP-Inspector:直接失效

FP-Inspector是2021年发表的一个混合检测器(静态 + 动态分析),使用OpenWPM爬虫采集JS执行日志,提取静态和动态特征,训练两个机器学习模型,通过OR操作输出结果。

论文尝试复现FP-Inspector,结果发现:它依赖Firefox 52 ESR,而这个版本根本不支持WASM。

所有WASM转换后的脚本都无法被FP-Inspector的爬虫处理——它们根本不会被加载,更不用说提取特征了。FP-Inspector不是被“绕过”,而是被“废弃”了。

这是一个极具讽刺意味的发现:一个2021年发表的SOTA检测器,在2025年因为依赖过时的浏览器而完全失效。这凸显了动态分析工具的一个根本脆弱性:它们的有效性高度依赖于底层浏览器的兼容性。

现代动态分析还有希望吗?

为了回答这个问题,论文使用Playwright + Chrome DevTools Protocol (CDP) 实现了一个简单的动态分析工具。它在引擎层设置断点,监控 navigator.userAgentnavigator.hardwareConcurrencyscreen.widthHTMLCanvasElement.prototype.toDataURLAudioContext.prototype.createAnalyser 等高熵API的调用。

结果:

  • 在DeepFPD的18个JS指纹脚本上,检测到5个。
  • 在这5个脚本的WASM版本上,同样检测到5个。
  • 在论文的受控数据集(124个脚本)上,检测到3个(WebGL、Canvas、Navigator & Screen)及其WASM版本。

虽然检测率不高(约28%),但关键是:WASM转换完全没有影响检测效果。 这证明了API层监控的鲁棒性:无论你的代码是JS还是WASM,只要你调用了被监控的API,就会被检测到。

四、商业工具:毫发无损

论文测试了10款浏览器扩展和5款浏览器的内置防御。

扩展

  1. CanvasBlocker – Fingerprint Protect (39,075用户)
  2. JShelter (10K+用户)
  3. uBlock Origin (20M+用户)
  4. Privacy Badger (1M+用户)
  5. Chameleon (8,436用户)
  6. Canvas Fingerprint Defender (80K+用户)
  7. Random User-Agent (Switcher) (100K+用户)
  8. Disconnect (400K+用户)
  9. Trace (20K+用户)
  10. Browser Fingerprint Protector (2K+用户)

浏览器

  1. Brave (阻止部分脚本)
  2. Firefox (严格模式)
  3. Chrome (无内置防御)
  4. Edge (无内置防御)
  5. Opera (无内置防御)

结果:所有工具都完全不受WASM转换的影响。

原因很简单:这些工具不分析代码,它们在API层面工作:

  • CanvasBlocker、Trace、Chameleon:拦截Canvas/WebGL API,返回随机化或规范化的输出。无论调用者是JS还是WASM,拦截都会生效。
  • Firefox严格模式、Brave:同样在API层添加噪声或阻止访问。
  • Privacy Badger、Disconnect、uBlock Origin:基于域名黑名单阻止第三方追踪器。(论文的测试是本地托管的页面,这些工具不会触发,这是测试设计的局限,并非工具的弱点。)

这个结果的含义非常深刻:API层防御是对抗代码混淆的唯一有效手段。

纯WASM指纹:唯一的例外

论文还测试了Guri等人在2025年提出的纯WASM指纹技术。该技术不调用任何传统的指纹API(Canvas、WebGL、Audio),而是通过测量WASM和JS之间的执行时间差异来区分浏览器。

结果:所有API层防御都失效了。 CanvasBlocker、Firefox严格模式、Brave都无法检测或阻止这项技术,因为它根本不调用那些被监控的API。

但论文也指出:这只是一个概念验证,目前仅此一项已知的纯WASM指纹技术,无法进行更广泛的评估。此外,该技术的实用性存疑——它只能区分Chromium内核和非Chromium内核的浏览器,无法进行细粒度的设备识别。

五、单条规则不够

论文进行了一项消融实验:每次只应用一条转换规则,观察其对DeepFPD检测效果的影响。

结果:任何单条规则都无法显著影响检测效果。

在DeepFPD测试集上,单独应用任何一条规则后,JS版本和WASM版本的分类指标(准确率、精确率、召回率)完全一致。这说明:检测逃避不是某一条规则的功劳,而是多条规则组合作用的结果。

单条规则的性能数据:

  • replace_with_regex:成功率最高(54.10%),但覆盖率极低(0.0004%),代码膨胀只有3.25%。
  • replace_canvas_api_calls:代码膨胀最大(199.84%),但成功率很低(16.79%)。
  • replace_func_defs(LLM驱动):成功率最低(9.70%),转换时间最长(3.40秒)。

这个结果支持了论文的贪婪转换策略:你需要尽可能多地转换代码,才能最大化地破坏检测器所依赖的特征。

六、实战建议

给指纹检测/反追踪开发者(防守方)

  1. 放弃基于代码特征的检测,转向API层防御。 DeepFPD和FP-Inspector的失败证明了一个残酷的事实:只要攻击者能够混淆代码(无论是JS混淆还是WASM转换),基于AST、CFG、词元序列的检测器就会失效。你可以用WASM数据重新训练模型,但这是一场永无止境的军备竞赛——攻击者总能找到新的混淆方式。API层防御不受代码结构影响,是唯一可持续的方案。
  2. 如果你坚持使用代码分析,至少要支持WASM。 FP-Inspector因依赖Firefox 52 ESR而完全失效,这是一个教训。你的工具链必须支持现代Web技术(WASM、ES2020+、WebGPU等),否则会被技术演进淘汰。但即使支持WASM,静态分析WASM二进制也极其困难——没有高级语义,只有低级指令。
  3. 动态分析 + API层监控是最有前景的方向。 论文的简单CDP工具虽然检测率不高(约28%),但完全不受WASM转换影响。如果你能在浏览器引擎层监控所有高熵API(Canvas、WebGL、Audio、Navigator、Screen、Storage等),并使用启发式规则(例如“短时间内调用多个高熵API = 指纹行为”),这个方案将比任何基于代码特征的检测器都更鲁棒。
  4. 关注纯WASM指纹技术的发展。 Guri等人的基于执行时间的指纹技术是一个警示:如果攻击者完全不调用传统的指纹API,而是利用WASM的微架构特性(执行时间、功耗、缓存行为),现有的API层防御将完全失效。你需要监控WASM模块的执行模式——例如,“一个WASM模块在短时间内执行了大量计算密集型操作”可能是一个可疑信号。

给指纹追踪服务商/攻击者(攻击方视角)

  1. WASM混淆是一个低成本、高收益的逃避策略。 论文的转换管道拥有85.76%的成功率、3.45秒的转换时间和24.54%的代码膨胀——这些成本都是可接受的。而收益是:学术检测器的召回率腰斩,某些检测器直接失效。如果你的指纹脚本将部署给数百万用户,一次性的转换成本完全值得。
  2. 但不要指望WASM混淆能绕过商业工具。 CanvasBlocker、Firefox严格模式、Brave这些工具在API层工作,WASM转换对它们完全无效。如果你的目标用户群体中有大量使用这些工具的人(例如隐私敏感的技术用户),WASM混淆不会带来任何优势。
  3. 纯WASM指纹是未来的方向。 如果你能实现不依赖传统指纹API的WASM指纹技术(基于执行时间、功耗、缓存、SIMD指令执行差异等),你可以绕过所有现有的防御。但这需要深入的微架构知识和大量实验。Guri等人的技术只是一个开始。

给浏览器厂商和标准制定者

  1. 考虑在WASM层添加权限控制。 目前WASM模块可以无限制地执行计算密集型操作,这为基于执行时间的指纹技术提供了空间。可以考虑:(1) 限制WASM模块的执行时间或CPU配额;(2) 要求WASM模块声明其计算意图,浏览器可据此添加噪声或限制精度;(3) 在开发者工具中提供“WASM执行监控”功能。
  2. API层防御应成为浏览器的默认行为。 Firefox的Canvas随机化、Brave的指纹保护证明了API层防御的有效性。Chrome和Edge应当跟进,将这些防御作为默认功能(而非需要用户安装扩展)。这是对抗指纹追踪最有效的手段。
  3. 建立WASM指纹检测的标准数据集和基准测试。 论文的数据集(750万JS脚本 + 124个配对脚本)并未公开,这限制了研究的可复现性。浏览器厂商和学术界应合作建立一个公开的、持续更新的WASM指纹数据集,以便研究人员测试和比较不同的检测方法。

对于更多此类WebAssembly相关的安全技术探讨、代码混淆实战以及浏览器开发者社区的最新动态,欢迎保持关注与交流。




上一篇:GDB Python脚本实战:如何在函数返回特定值时自动暂停
下一篇:谷歌Antigravity额度告急别焦虑,三招让你实现AI算力自由
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-4 19:37 , Processed in 0.481819 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表