返回知识中心
指纹

JavaScript 栈深度:浏览器隐私信号

了解 JavaScript 递归限制为何会因执行上下文而变化,如何负责任地审查这一信号,以及 BotBrowser 能够控制哪些行为。

BotBrowser Team

文档中心

想直接看维护中的产品文档?

这篇文章对应的主题已经有文档中心页面。需要规范流程、当前参数和长期参考时,优先看 docs。

JavaScript 递归在抛出 RangeError 之前能达到的深度是一个可观察的小数值,会随浏览器版本、宿主和上下文变化。BotBrowser 的 --bot-stack-seed 决定该限制如何得出(profile、real 或整数种子),但不会改变操作系统原生的栈分配,也无法控制第三方页面如何使用这个计数。

要点摘要

按配置文件记录的栈行为审查用 profile,描述宿主用 real,需要可重复的变化用整数种子,不要强行指定任意数值。在主窗口和 Worker 中各运行一个有界递归测试,并记录计数、上下文和版本。

本文目录

JavaScript 递归有实际限制。当函数不断调用自身时,浏览器最终会停止调用并报告栈溢出,通常表现为 RangeError。错误出现前成功调用的次数属于可观察的应用行为。Web 平台没有规定一个统一数值,因此不同浏览器实现、版本、操作系统和执行上下文可能得到不同结果。BotBrowser 可以在隔离的受控上下文中用同一份配置运行栈深度检查,但不能让其他构建、平台或 JavaScript 引擎上的深度完全相同。

这种差异与隐私有关,因为页面可以执行小型递归测试,并把结果次数作为浏览器指纹的一个输入。这个次数不是姓名、账户凭据,也不能证明某人的位置。它只是一个低容量的技术信号,和许多其他观察结果结合时可能增加一点区分度。把它视为单一信号,可以避免把兼容性细节夸大为身份结论。

对产品团队有用的问题不是如何收集更丰富的画像,而是数值变化时如何保持应用行为可预测,以及如何选择与正在审查的配置文件一致的浏览器配置。优先提供回退路径的应用,即使递归测试不可用、结果不确定或与上次会话不同,也应继续工作。任何面向用户的任务都不应依赖某个特定深度数值。

在多个执行上下文审查浏览器递归检查,并保留优先回退的应用策略

这个测量代表什么

每次 JavaScript 调用都会向调用栈添加一个帧。帧保存返回信息和执行状态,浏览器则负责管理栈周围的内存与安全检查。帧的形状很重要:简单递归函数得到的次数可能不同于带有更多参数或局部值的函数。因此,测量结果既描述测试本身,也描述运行它的上下文。只有测试形状和周围页面状态保持稳定时,次数比较才有意义。

最终抛出的错误可以防止无限递归。捕获错误能让应用完成检查,但不会移动限制。JavaScript 不能为现有线程请求更大的原生栈,也不能改写浏览器的栈记账。脚本可以选择不运行检查,但这不会改变另一个页面在自己的上下文中能够观察到的行为。

操作系统栈分配是影响因素之一,却不是唯一因素。浏览器实现使用不同的帧布局、安全余量、优化路径和上下文初始化方式。浏览器更新可能改变其中任何细节。主机即使有充足的空闲内存,也可能得到不同递归次数,因为这个限制是运行时策略,而不是当前可用内存的实时测量。

主窗口和 worker 不一定以完全相同的栈条件启动。专用 worker 和共享 worker 有各自的初始化方式,WebAssembly 代码也有自己的执行规则。因此比较时应记录上下文名称和测试形状。主线程结果不应被说成是 worker 的承诺,worker 结果也不应被用来分类设备。

上下文和版本为何重要

页面在一次浏览器会话中可能观察到稳定值,但稳定不等于永久不变。浏览器版本、操作系统更新、安全加固和页面代码变化都可能影响次数。递归函数的小改动也可能改变结果。因此历史数值本身证据很弱,支持团队调查兼容性问题时应记录浏览器版本和测试修订号。

这一信号最适合作为自有测试中的一致性检查。例如,团队可以把主线程结果与应用实际使用的 worker 结果比较。这样测试回答的是所选配置文件对该应用是否符合预期,而不是识别未知访客、推断硬件或依据次数作出访问决定。

如果同一审查还测试媒体能力,可参考 MIME 与编解码器指纹指南,但应将该观察与栈深度计数分开。

尊重隐私的服务会让结果靠近真正需要它的决策。如果应用只需要判断递归较深的可选功能是否安全,可以使用本地的是否策略并丢弃原始次数。如果支持案例需要更多细节,应记录测试修订号、浏览器版本和所选策略,而不要把数字加入账户标识符。这样更容易删除,也更难成为跨会话标签。

具体对比记录应包含 browserVersion、profileId、fixtureRevision、context 和捕获的 RangeError;这些字段能解释次数变化,而不保存访客标识。

分析也应遵守同一边界。仪表板可以报告功能采用了基线路径还是缩减路径,而不要按栈深度排列用户。完成、取消和恢复等聚合结果,比长期保存技术数值更能说明体验。测量缺失或失败时,必要内容和控件仍应可用。

BotBrowser 的支持边界

BotBrowser 提供 --bot-stack-seed,用于选择配置文件所对应的可观察 JavaScript 递归限制来源。profile 使用所选配置文件关联的栈深度。real 保留主机的原生结果。整数种子请求确定性的会话变化,因此相同配置文件和种子可以提供可重复的审查输入。这些模式说明受支持的配置选择,并不承诺每个应用都会返回一个固定数字。

该设置用于让面向浏览器的信号与正在审查的配置文件保持一致。它可以把所选行为应用到主线程、Web Workers 和产品支持的 WebAssembly 上下文。团队仍应测量自己的每个应用上下文,因为 worker 与主窗口的执行条件不同。配置文件选择不能代替兼容性测试,测试结果也不能证明某人的身份。

BotBrowser 不会改变操作系统的原生栈分配,也不会消除一般的浏览器内存限制。所选行为针对页面可观察的递归限制,不会重新设计应用平常的调用栈使用。普通页面代码应远低于该限制,但主动进行深度递归的应用仍需自行进行性能、错误处理和资源测试。

BotBrowser 也无法知道第三方页面是否选择运行递归检查。它可以提供配置好的配置文件行为,但不能让页面忘记这个值、决定页面如何保存它,或替代网站的隐私政策。适当的产品做法是减少收集、解释可选诊断,并在数值缺失时保留可用的基线路径。

为审查选择模式

当目标是按特定配置文件记录的栈行为审查应用时,使用 profile。当团队检查相关浏览器表面在回归审查中是否保持协调时,这个模式很有用。把浏览器版本、配置文件和测试修订号一起记录,可以看出差异来自配置还是页面。

当测试要描述实际运行浏览器的主机环境时,使用 real。它适合普通应用调试、容量工作,或明确代表当前机器的兼容性报告。它不会让结果自动适用于另一台主机,也不能说明当前主机代表所有受支持环境。

当需要可重复的变化、又不想手写深度数值时,使用整数种子。种子是浏览器配置输入,不是页面应暴露的标识符。把种子与配置文件和版本保存在受控测试记录中,避免放进用户账户、网址或分析字段。不同种子可能产生不同结果,因此只比较声明了相同输入的运行。

不要选择模式来强迫应用达到任意数字。极端或未记录的值可能让递归信号与其他配置文件表面不一致,也可能掩盖真实应用缺陷。更稳妥的问题是所选配置文件和版本是否在应用负责的上下文中产生合理、可重复的结果。如果深度递归工作负载需要更多空间,应使用原生模式和普通资源监控单独验证。

可重复的应用审查

从一个小型夹具开始,让有界递归函数运行并捕获预期溢出。只记录审查需要的次数、夹具运行的上下文和浏览器版本。先在主窗口运行,再在应用实际使用的各个 worker 上运行。夹具不应枚举无关浏览器属性,也不应把技术清单发送到服务器。

在干净启动浏览器后,用相同配置文件和种子重复夹具。可重复结果能说明配置加载保持一致,但不是生产保证。如果结果变化,应先检查配置文件路径、版本、worker 初始化和夹具修订号,再下结论。页面包改变递归函数的帧形状,即使浏览器配置未变,也可能改变结果。

要像检查成功路径一样检查失败路径。如果递归不可用、夹具抛出意外错误或 worker 无法启动,应用仍应保留必要内容并提供易懂的回退。可选增强可以延后或禁用,导航、账户控件和可访问状态信息仍应可用。对用户而言,这种行为比原始深度数字更重要。

跨主机审查时,应先比较声明的输入再比较结果。配置文件名称相同还不够,因为浏览器版本、功能开关、夹具代码或上下文可能不同。记录保持简短即可:配置文件标识、模式、种子类型、版本、上下文、应用策略和结果。不要因为测试暴露了一个技术数值,就保存详细硬件描述。

浏览器指纹概览解释许多小信号如何组合。Navigator 属性指南说明报告的浏览器属性应与身份结论分开。若夹具在多个上下文运行,可对照浏览器 API 完整性指南。这些链接提供背景,不会把栈深度变成独立标识符。

审查行应标明 profile、real 或整数 seed,并用 page、Worker、WebAssembly 标记实际测量上下文。

读懂发生变化的计数

两次运行之间变化的东西通常意味着下一步检查
只有浏览器版本帧布局或安全余量变了用旧版本再跑一次确认
只有夹具函数帧形状变了,不是浏览器变了锁定夹具修订号后再比较
只有上下文主线程和 worker 从来不是同一个值让每个上下文跟自己的历史比
同一台机器上全都变了某个参数或配置文件路径没有生效启动前打印实际生效的命令行

计数变化有四种常见解释,而计数本身无法区分它们。两次运行只差其中一个变量,才回答了一个问题;差了好几个,则什么也说明不了。把四项输入都写下来,然后只改一项。

先固定夹具。把递归函数存成带版本的文件,并把修订号打印在结果旁边,因为参数个数、闭包或新增的 try 块都会改变帧形状,从而改变深度。夹具本身漂移,是所谓「回归」最常见的原因,实际上只是改了页面。

其次固定版本。比较完整的构建串,而不只是主版本号,因为这个限制是运行时策略,会随安全加固和代码生成变化。当只有版本变了,用旧构建再跑一次就能把真实变化和运行间噪声分开,而重复跑新构建做不到这一点。

第三,让每个上下文跟自己比。主线程数字和 worker 数字本来就不该相等,一对四舍五入后的数值无法说明任何一个上下文是否稳定。应在同一构建上分两次分别记录每个上下文。

如果这三项输入完全一致而数字仍然变了,请在归咎浏览器之前打印实际生效的命令行。启动脚本里有、进程参数里没有的参数,或者在第二个 shell 里解析到另一个文件的配置文件路径,都会造成看起来像浏览器差异、实际不是的变化。

一份短记录能让这件事事后可查:

release=142.0.7444.60
fixtureRevision=3
mode=profile
context=main   depth=10789  RangeError
context=worker depth=10761  RangeError

相隔一个版本的两份这种记录,就足以判断四种解释里是哪一种。而从控制台复制出来的单个数字做不到。

限制与隐私决策

栈深度的熵有限,应作为辅助证据。它会随代码形状和运行时条件变化,不能单独可靠地识别浏览器、设备或个人。服务无限期保存它,带来的隐私风险可能超过产品价值。优先使用短期策略决定、明确的保留期限,并在审查或支持案例结束时删除。

不要把递归次数用于授权、欺诈证明、位置证据或账户安全替代品。它不能告诉应用访客是否可信,也不能证明两个会话属于同一人。身份验证、速率控制和滥用处理需要自己的服务器证据,不应依赖偶然的 JavaScript 限制。

不要承诺所有浏览器系列、操作系统、worker 实现或 WebAssembly 运行时的行为都相同。公开文档可以解释预期范围,兼容性测试则确定特定产品支持什么。版本改变观察次数时,应更新测试记录并调查用户可见结果,再决定是否改变配置文件策略。

清晰的支持说明应同时写出边界两侧:BotBrowser 可以为受支持上下文选择配置文件、原生或种子栈行为,但不能修改主机分配、保证应用结果,也不能控制第三方页面记录什么。这样的说法可验证,并给操作人员一个具体下一步:测试自有流程、保留回退,只保存该决策所需证据。

将递归结果变化描述为兼容性观察,而不是新的身份类别。注明比较的浏览器版本、配置文件模式、上下文和夹具修订号。如果页面仍能完成任务,可能无需面向用户的改动。如果可选递归功能变得不可靠,应调整功能策略和恢复路径,而不是扩大收集或给访客贴标签。记录还可以写明可见回退、仍可执行的用户操作和审查数据保留期限。这样未运行夹具的支持人员也能理解事件,并以页面是否完成任务或解释有界回退作为验收点。

支持团队可以把配置证据与应用证据分开。配置证据说明选择的配置文件、模式、种子类型和浏览器版本。应用证据说明页面是否加载、递归工作是否完成以及回退是否可用。分开保存可防止技术次数变成隐藏账户属性,也让后续调查无需客户数据即可重复。

夹具还应测试取消和导航。不再需要的递归操作应在用户离开视图时停止,关闭的 worker 不应继续安排工作。这些是普通生命周期测试,却能防止栈深度实验造成额外资源消耗。它们也让回退更有意义,因为页面可以放弃可选工作而不阻塞必要控件。

可访问性也属于同一审查。worker 不可用或可选增强失败时的状态消息应提供给辅助技术,回退应保留键盘操作、可读文本和同一项必要操作。技术测量不能代替可访问体验。只记录所选策略和可见结果,团队就能改进这些状态而不保存底层递归次数。

改变配置文件策略前,应比较多个普通原因:浏览器版本、worker 启动路径、递归函数变化、安全设置或主机配置。单独的次数无法区分这些原因。带有命名夹具和有界回退的短清单,比反复采样更可靠。自有应用行为正确后,停止收集该值,只保留支持流程需要的决策记录。

这些做法让栈深度保持狭窄而可辩护的作用。它可以帮助团队审查所选浏览器配置文件在自有上下文中是否协调,同时由应用设计负责韧性、隐私和可访问性。最终边界是可测试的:有意配置浏览器,只测量审查需要的内容,并在信号变化时保持完整的用户体验。

审查记录应设置明确的有效期。只在兼容性问题仍未解决时保留配置文件、版本、测试修订、上下文和可见结果;原始计数不再支持决策后就删除,并把回退行为写入验收说明。这样技术观察会始终绑定于短期工程任务,而不会变成账户属性。

问题仍开放时,期限记录可保留 retentionDays、fallback、outcome、release 和 fixtureRevision,随后删除原始计数。

公开来源

实用结论

按审查问题选择模式,不要把计数当作授权、欺诈证据或位置证据。结果变化时,先比较浏览器版本、Worker 启动路径、测试代码修订和宿主,再决定是否修改配置文件策略,并保证回退路径可用。原始计数只在兼容性问题未结案期间保留。

#Stack-Depth#浏览器指纹识别#Recursion#Javascript#隐私

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

让 BotBrowser 从研究走向生产

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