返回知识中心
指纹

CreepJS 与 Android WebView 检测:信号边界

按 HTTP、页面 JavaScript、worker 和应用桥接梳理 Android WebView 信号,说明 CreepJS 源码检查什么、又不能证明什么。

BotBrowser Team

文档中心

想直接进入 指纹 文档吗?

这篇文章属于知识中心的指南库。若你要步骤化配置、参考说明和持续更新,请直接进入对应 docs 分区。

BotBrowser 支持在获授权的兼容性检查中使用声明的 Android WebView 身份,但不能判断某个私有反机器人服务会如何分类嵌入式浏览器。WebView 配置使用显式提供、含 wv 的 --user-agent 模板,不会自行选择特定应用的 X-Requested-With 值。更有用的问题是:每个值来自哪个层,页面实际做了哪些比较?

TL;DR

  • User-Agent 中的 ; wv 是 WebView 家族线索,不是某个应用的证明。
  • X-Requested-With 和 window.android 取决于宿主应用行为,两者都不是通用信号。
  • 固定版本的 CreepJS navigator 模块会比较部分页面与 worker 值,并读取 User-Agent Client Hints。
  • 值不一致只能说明两次观察有差异,不能确定原因,也不能证明自动化。

目录

信号层:HTTP 请求头、页面 JavaScript、worker 值和可选应用桥接
信号来源将浏览器观察值、应用身份和自动化结论区分开来。

观看视频

以下英文视频按请求头、页面 JavaScript、worker 值和可选应用桥接拆分 Android WebView 信号,并说明受控浏览器检查能证明什么、不能证明什么。视频不会认证宿主应用,也不是 Android WebView 真机复现。

宿主应用不等于浏览器表面

Android WebView 是由 Android 应用承载的浏览器组件。页面能观察浏览器属性和请求字段,宿主应用也可以选择 User-Agent、添加请求头,或暴露 JavaScript 桥接对象。这些选择彼此独立,作用范围也不同,应分别记录。

BotBrowser 的 Android WebView 文档介绍了其声明身份设置如何呈现带 wv 的 User-Agent 形式和可选应用请求头。检测问题更具体:检测器能从每个表面推断什么,还有哪些事情未知?身份设置与对齐见 Android WebView 配置文档;CreepJS 如何组合相互独立的观察,见 CreepJS 信号分组与观察边界。

每类信号来自哪里

信号可观察层可以支持什么单独不能证明什么
含 ; wv 的 User-AgentHTTP 请求和 navigator.userAgent当前呈现的是 WebView 风格 UA哪个应用嵌入了它,或这个值是否最新
Sec-CH-UA 和 navigator.userAgentData请求头与页面 JavaScript,视支持情况当前上下文暴露的品牌和平台信息所有 Android WebView 都暴露相同提示
navigator.platform页面 JavaScript平台家族观察值设备型号或宿主应用
WorkerNavigator 值worker JavaScript页面和 worker 的部分值是否一致任一值是否对应物理硬件
X-Requested-WithHTTP 请求,前提是应用发送应用可能提供了类似包名的请求头通用 WebView 标记或可信应用身份
window.android 或其他桥接对象页面 JavaScript,只有应用注入时才有宿主暴露了一个具名接口所有应用都暴露它,或调用其方法一定安全

Android 通过 WebSettings 提供 User-Agent,宿主可以修改它。Android 也把 addJavascriptInterface 记录为应用主动执行的操作,桥接名称由应用指定。因此,没有桥接对象不能排除 WebView;出现某个桥接名称也不等于认证了应用包。

请求头和 JavaScript 应记录在不同字段。服务器在页面脚本运行之前看到请求头;页面代码只看到自身浏览上下文的属性。重定向、service worker、应用包装层或版本变化可能只影响某一层,不改变其他层。

平台家族分类比 WebView 更宽

CreepJS 的 getReportedPlatform() 会把多个系统名称归并成较宽的平台家族。在 User-Agent 分支中,android、linux 和 cros 都映射到 Linux 分类;navigator.platform 分支也把 android、arm 和 linux 归在同一类。这种处理可用于检查一致性,但会丢失细节:Android 家族相符不能识别 WebView,也不能指出承载它的应用。

navigator 模块另有一项子串检查,会把 android 视为可识别的平台标签之一。这只是检查当前源码版本能否识别该字符串,不是判断字符串由哪个应用产生。原始字段、归一化后的家族和检查结果应分别保存。只留下 Android = true 会丢失值来自 UA、navigator.platform 还是请求头的信息。

这一区分适用于对比 WebView、Chrome Custom Tabs 和完整 Chrome。它们都可能运行在 Android 上,但浏览器身份与宿主应用行为的组合不同。宽泛的平台家族一致,不代表品牌、请求头或 JavaScript 桥接对象相同。

固定版本的模块不会把所有异常值都当成同一种发现。如果全局对象 chrome 存在,它会检查 navigator.userAgent 是否包含 navigator.appVersion;也会检查首尾空格、重复空格,并运行项目定义的 gibberish 文本检查。这些属于 User-Agent 字符串质量观察。随后代码另行比较页面 userAgent 与 worker 值,并分别标记上下文差异。

对于 deviceMemory,源码接受 0.25、0.5、1、2、4、8、16 和 32。若 performance.memory.jsHeapSizeLimit 可用,代码会把限制换算成 GB,并检查是否高于报告的设备内存;随后还会与 worker 的 deviceMemory 对比。hardwareConcurrency 有独立的页面与 worker 相等性检查。这些条件彼此不同,不是一个 Android 分数。

语言分支会比较 navigator.language 与 navigator.languages 首项的前两个字符。如果两个页面字段都为真值,源码在比较后就返回格式化的语言结果,不论前缀是否匹配。只有未进入这一分支时,代码才会执行与 worker 语言字段的比较。plugins 检查则把 navigator.plugins 和 navigator.mimeTypes 配对检查。兼容性记录应写明具体属性及触发条件,而不是把无关检查合成一个 WebView 标签。

Client Hints 有可用性分支

如果 navigator.userAgentData 或 getHighEntropyValues() 不存在,CreepJS navigator 模块不会返回 User-Agent Client Hints 结果。两者都存在时,源码请求 platform、platformVersion、architecture、bitness、model 和 uaFullVersion,再与低熵的 brands、mobile 字段组合。

输出前,代码会排除名称包含 Not 的品牌;当品牌列表有多个值时,还会从展示列表中移除 Chromium。这是库输出格式的一部分,不能据此证明浏览器中不存在 Chromium,也不能推断所有平台都会提供相同的高熵字段。

应区分三种情况:API 不存在、请求字段被省略,或 API 返回了字段值。所引来源说明 Client Hints 有条件分支,但不能确定某个字段被省略的原因。跨上下文对比前先记录观察状态,否则容易把缺失提示误判成不一致的提示。

固定版本的 CreepJS 源码检查什么

公开 CreepJS 源码快照固定到提交 10aa6724cd33a1015db1574211890518cd04f0cc。navigator 模块把 Android 列在多个可识别的 navigator.platform 子串中。它也会比较从 User-Agent 解析的系统家族与页面报告的平台。这属于一致性检查,不是专门识别 WebView 的规则。

同一模块还会将页面 navigator 的部分值与 worker scope 中的值比较。源码包含 platform、userAgent、deviceMemory、hardwareConcurrency、language 和 languages 等比较;语言与 worker 的比较位于两个页面语言字段都为真值的分支之外。若 User-Agent Client Hints API 可用,它会请求 platform、platformVersion、architecture、bitness、model 和 uaFullVersion 等字段。这些是固定版本 navigator 模块中的实现细节,不代表每个 Android WebView 都提供全部字段。

独立的 headless 模块会报告多组浏览器可见条件。不能把这些分组和输入压缩成 Android WebView 结论;CreepJS headless 检测分析说明了这些分组为什么回答不同问题。

这些代码支持一个有限结论:被检查的 CreepJS 模块能够比较部分浏览器表面,并报告若干 headless 相关条件。它们并未认证创建 WebView 的 Android 应用包,也没有揭示第三方私有决策规则。

把 WebView 证据与其他结论分开

兼容性报告使用四种标签,而不是一个二元结论:

报告标签最低证据谨慎解释
Android 家族上下文相互一致的 Android 平台与 UA 观察值可见上下文报告为 Android 家族
WebView 风格上下文WebView 风格 UA 或品牌,同时分别记录请求和页面上下文呈现了一个或多个 WebView 相关属性
上下文差异指定的页面/worker 或请求头/JavaScript 比较不一致本次运行里的两项观察值不同
自动化结论独立证据和有文档记录的决策规则单个页面信号不足以得出结论

在查看浏览器品牌对齐时,这个区分很重要。品牌、平台和嵌入式应用行为有关联,但不是可以互换的字段。navigator.platform 变化不能解释请求头变化,除非同一份记录把两者关联起来并保留各自来源。

差异只证明值不同

在这份源码中,page/worker 差异只说明被比较的值在两个观察上下文中不同。它不能确定某个浏览器版本应返回哪个值,也不能指出差异原因。源码描述的是自身比较逻辑,不会认证应用、操作系统、WebView provider 或第三方策略。

报告里应把四个陈述分开:观察值及其来源层;字段是否存在且可读;具体是哪项比较不同,例如页面 navigator.platform 与 worker 的 platform;以及对自有兼容性测试可以得出的有限影响。更广泛的自动化结论需要独立且有记录的决策规则。

不要把一个字段的发现默默扩展到整个会话。X-Requested-With 请求头发生变化,本身不能说明 navigator.userAgentData 如何变化;worker 不一致也不能说明应用桥接对象的状态。保持边界能保留诊断信号的价值,避免声称测量没有证明的身份。

字体结果也是独立表面:CreepJS 字体表分析说明了为什么检测到的字体组不能识别承载 WebView 的应用。

隐私影响也很具体:window.android 可能暴露应用方法,Android 警告 JavaScript 桥接会扩大安全边界。检测审查只应记录桥接是否存在,不要为识别容器而调用无关方法,也不要收集测试不需要的应用数据。

自有应用的受控对比方法

对于自己拥有的应用,在同一 Android 版本和应用构建上比较三个上下文:应用内 WebView、产品使用时的 Chrome Custom Tab,以及完整 Chrome。保持页面源站和测试动作不变。下表是采集计划,不是某次运行的报告:

采集点记录分开保留
请求User-Agent、Client Hints 请求头以及存在时的 X-Requested-With首次导航与后续请求
页面navigator.userAgent、navigator.platform、可用的 userAgentData 字段缺失、拒绝访问与空值是不同状态
Worker对应的 WorkerNavigator 字段worker 创建时间与源站
宿主应用是否有意注册桥接及其文档化名称不要仅为识别容器而调用方法

把 Android、WebView provider、应用构建和测试页面的版本一起记录。provider 版本不同会让正常升级看起来像身份不一致。每个不同的单元都要记录来源层,以及字段属于缺失、不可用还是值不同;不要把这些状态合成一个分数。

保留上下文并缩小数据采集范围

使用自有页面上的同一操作、同一源站和可比较的导航时机。记录 Android、WebView provider、应用与浏览器版本、语言、权限状态,以及上下文类型:应用 WebView、嵌入式 Chrome 标签页或完整 Chrome。这些字段用于解释对比,不是判定结论。更新后要同时保留新旧版本,不能把新值称为谎报。

X-Requested-With 可能包含类似应用包名的标识,只有兼容性问题确实需要且得到授权时才保存。共享报告通常只需标记它是否存在,或对值进行脱敏。JavaScript 桥接可能开放应用方法;记录应用是否有意注册桥接及其文档名称即可,不要调用测试无关的方法,也不要序列化应用数据。Android 对 addJavascriptInterface 的警告同样说明了安全边界。

每项比较都要关联到来源:请求抓取对应请求头,页面抓取对应 navigator,worker 抓取对应 worker 值。精简记录只需字段名、状态、版本和被比较的一方,无需保存用户内容。这样足以复核自有应用兼容性,并减少意外收集包标识符或应用数据。

对齐请求发生的时点

把顶层导航与后续子资源请求分开采集。应用可能只给部分请求添加请求头,因此不能把某张图片请求的请求头与最终文档的 JavaScript 值当作同一次观察直接比较。每条请求都关联导航、源站、应用构建、浏览器版本和权限状态。发生重定向时记录每一跳,不要把首个和最终 URL 合并成一笔事务。对比结果只描述提供该结果的上下文和时点,不代表所有相同 Android 主版本的设备。

常见问题

; wv 能证明请求来自 Android WebView 吗?

不能。这是 User-Agent 中可识别的字符串,应用可以配置它。它描述的是当前呈现的值,不能证明宿主应用包或请求来源。

CreepJS 会识别承载页面的 Android 应用吗?

这里引用的 navigator 检查会比较页面可见值与 worker 值,但不会认证宿主应用包。由应用注入的桥接对象或请求头取决于具体应用,不是 CreepJS 通用的身份信号。

页面与 worker 不一致意味着浏览器是自动化的吗?

不意味着。它只表示记录到的值不同。浏览器更新、API 支持情况、上下文时间或应用配置也可能造成差异。扩大结论前先保存确切字段与环境。

来源

实践结论

把 WebView 识别当作信号来源问题,而不是检查单个 token。分别记录请求头、页面属性、worker 对比和可选桥接。对于自有应用 QA,应比较等价上下文并保留缺失值;差异是有用的兼容性发现,但不能证明设备身份或第三方的反自动化判断。

#CreepJS#Android WebView#浏览器信号

本文属于指南: 什么是浏览器指纹?原理、风险、信号与隐私边界

让 BotBrowser 从研究走向生产

先用这些指南理解模型,再进入跨平台验证、隔离上下文和面向规模化的浏览器部署。