Infinitoai与Turnstile:一次浏览器验证机制的技术拆解
Note
一个"会点复选框"的脚本和一个"看起来像真人"的脚本, 差距究竟在哪一层? 是注入时机, 是事件来源, 还是页面状态感知?
0x00 背景
原文以开源项目 Infinitoai(一个 Chrome 扩展)为切入点, 不讲"怎么一把梭", 而是拆解真实浏览器里的扩展为什么比普通脚本更接近真人环境. 它值得被单独拆解的三个理由:
- 它把"验证机制"从"点没点到"提升为"环境 + 输入 + 上下文 + 状态变化"的综合判断, 这是理解一切浏览器验证(包括 Turnstile/reCAPTCHA)的统一模型.
- 它给出的三个注入条件(
document_start/MAINworld /all_frames)是浏览器逆向中最容易被忽略、却直接决定成败的上下文问题. - 它提出的"可信事件" vs "普通点击"、"状态机" vs "固定等待"、"可验证信号" vs "界面变化", 是通用自动化工程方法论, 与 AI Agent 设计直接相通.
边界说明: 原文本身是机制研究, 本文同样只停留在机制层的解释, 不提供可执行绕过步骤.
0x01 核心结论
浏览器验证的本质, 早就不只是"点没点按钮", 而是对环境、输入、上下文和状态变化的综合判断.
普通脚本很脆、无头环境容易暴露, 真实浏览器里的扩展更接近"自然用户环境", 因为:
- 更真实的浏览器指纹: 真实 Chrome 内核 + 真实渲染栈.
- 更接近自然用户的 API 行为: 页面脚本与扩展共享真实运行环境.
- 可以借助浏览器内部能力派发输入事件: 比
element.click()/dispatchEvent更底层的输入链路.
原文拆出的五个工程思路(这是全篇骨架):
- 尽早注入, 抢执行时机 — 关键补丁注入晚了就失去意义.
- 进入正确上下文 — 主世界/隔离世界/iframe/子 frame 的差别直接影响结果.
- 输入不只讲"能点", 还讲"像不像" — 事件属性、触发链路、坐标关系.
- 建立状态机, 而不是迷信固定等待 — 固定 sleep 脆得像纸.
- 用可验证信号判断成功 — 只看 UI 动画, 不如看真实状态变化.
0x02 关键细节
2.1 项目结构透露的分层思路
Infinitoai 不是单点技巧, 而是分层设计:
background.js: 流程编排.- 内容脚本: 与页面交互.
- 专门的补丁脚本: 在页面很早阶段注入.
- 侧边栏: 控制面板.
这说明成熟浏览器自动化项目的共性:把"环境修补、页面感知、事件派发、结果判断"拆成几层, 而不是一个暴力脚本硬点.
2.2 为什么要在页面早期修补事件坐标
Turnstile 会观察鼠标事件的 clientX / clientY / screenX / screenY. 真实用户点击时这些值之间有合理关系; 程序构造的合成事件常有奇怪特征:
screenX / screenY接近 0.- 坐标之间关系不自然.
- 每次都机械一致.
所以需要在页面脚本执行前尽早注入补丁修正坐标属性. 原文强调的三个注入条件:
{
"run_at": "document_start",
"world": "MAIN",
"all_frames": true
}
document_start: 越早越好, 赶在页面主要逻辑运行前.MAIN: 进入页面主世界, 而不是只活在扩展自己的隔离环境.all_frames: 验证组件常在 iframe 里, 不进 frame 基本白搭.
这组配置本身就是对"验证机制对上下文位置有多敏感"的最好说明.
2.3 "可信事件"比"普通点击"更重要
普通前端触发方式(element.click(), dispatchEvent(new MouseEvent(...)))在很多业务场景够用, 但反机器人验证不仅看"有没有发出点击", 还看点击是否足够可信:
- 事件是否来自浏览器内核真正接收的用户操作.
- 坐标关系、触发链路、时序是否自然.
Infinitoai 的思路是借助浏览器本身更底层的输入能力(CDP / 扩展能力)完成输入, 让整条输入链路更像浏览器内核接收到的一次真实操作, 而不是只追求"事件触发成功".
这对 AI Agent 自动化的启示: 当目标系统的反检测只看"行为外观"时, 用真实浏览器能力派发事件, 优于在 JS 层伪造事件.
2.4 页面状态机: 知道自己处在哪
自动化脚本失败的主因不是"不会点击", 而是"不知道自己现在处在什么页面状态". 同一流程里页面可能处于:
- 正常表单页 / 全页验证页 / 内嵌验证组件页 / 广告弹窗页 / 错误页 / 验证完成后的业务页.
固定等待(打开页面 -> 等3秒 -> 点击 -> 等5秒)在页面慢一点、结构变一点、弹额外层时就会乱套. 更稳的做法:
先判断当前页面属于哪种状态
-> 再决定该点击、该等、该继续, 还是该终止
这就是把"流程"升级为"状态机" — 与 LLM Agent 中"先感知当前 tool 执行状态再决定下一步"同构.
2.5 结果判断: 不能只看界面变化
很多初学者盯着界面看: 勾选框亮了没、文本变了没、页面跳转了没. 这些都可能不稳. 更靠谱的是找验证通过后的关键状态信号:
- 隐藏字段里出现有效 token.
- 页面 DOM 出现成功标记.
- 后续业务区域解锁.
- 网络请求状态变化.
真正稳的判断标准是"页面内部已经出现可验证的成功状态", 而不是"看起来像成功了". 这对应到 001-CF过盾工程 中"双重成功判断 + 等待 Cookie 写入"的工程细节.
2.6 局限
这类方案不是万能钥匙, 前提条件很强:
- 必须在真实浏览器环境中运行.
- 必须依赖扩展能力.
- 必须贴近页面上下文.
- 必须对具体站点流程有较强适配.
离开真实浏览器, 很多前提就不存在了. 它更像一种浏览器交互研究样本, 而不是适用所有网站、所有环境的通用模板.
0x03 拓展升华展望
你的自动化流程, 是"会执行动作", 还是"像真实用户一样被系统感知"?
- 如果你只追求"事件触发成功", 那么
element.click()就够 — 但检测方也在看你的坐标、时序和上下文. - 如果你追求"输入链路、页面状态、结果信号"三者的自洽, 那你就已经把验证机制当成一个需要逆向建模的系统, 而不是一道题.
把一个"会点复选框"的脚本和一个"看起来像真人"的脚本放在一起比, 先站到更高一层看: 浏览器验证的判据早就不在动作本身, 而在环境、输入、上下文与状态变化是否互相自洽.
事实层面 … 原文的五个工程判断都能在 Infinitoai 的代码结构里找到落点: document_start / MAIN world / all_frames 三个注入条件是浏览器扩展机制下的客观约束, 可信事件与普通点击的区别在于输入是否经由浏览器内核接收, 页面状态机与可验证成功信号则来自原文对失败案例的归纳.
个人判断 … 这套五块拼图不会停留在"过验证"这一件事上. 当 Agent 开始操作真实网页, 它面对的是同一套判据: 谁在感知页面状态、输入链路是否可信、成功信号是否可验证, 决定了自动化能否从一个会执行动作的脚本, 变成被系统当成真实用户来感知的流程. 我倾向于认为, 后面真正稀缺的不是更隐蔽的注入技巧, 而是把"环境、输入、上下文、状态、信号"这五件事写成一套可复用观察框架的能力.
0x04 参考来源
- 原文: Infinitoai 与 Turnstile: 一次浏览器验证机制的技术拆解, nax 的博客, 2026-04-18. 本文的五个工程判断、三个注入条件与项目结构描述均出自这里.
- 相关: 本分类 001 的 CF 过盾工程 展示了"状态机 + 可验证信号"的具体工程实现(双重成功判断、等待 Cookie 写入).
- 相关: 本分类 004 的 Turnstile 防御面分析 从防御侧解释了"为什么可信事件 / 上下文位置 / 状态判断"会被检测.

