BrowserScan Bot Detection CDP 技术报告
结论
目标页 https://www.browserscan.net/zh/bot-detection 的 CDP 相关检测不是哈希类纯算,而是两条浏览器运行时探针:
CDP:利用Error.stackgetter 被console.debug(error)序列化时触发的副作用。Dev Tool:在Worker中执行debugger,用before/after两次消息的时间差判断是否被开发者工具或调试协议暂停。
可复刻代码已写入 browserscan_bot_cdp_detection.js。它保留 BrowserScan 的判定阈值:Dev Tool 时间差大于 100ms 判定异常,after 消息 500ms 内未返回也判定异常。
源码定位
本次分析使用已落盘响应脚本:
- 页面组件:
trace_browserscan_bot_20260609_221758/response_9684_8589934601_7.BtL2bcz0.js.decoded.js - 检测函数:
trace_browserscan_bot_20260609_221758/response_9684_8589934602_2.hd4NLkmt.js.decoded.js
组件导入关系:
js
import {
r as He,
s as Ve
} from "./hd4NLkmt.js";
页面执行关系:
js
f.value = He(); // CDP 卡片
y.value = yield Ve(); // Dev Tool 卡片
if (f.value) n.value = false;
if (y.value) n.value = false;
页面展示关系:
f控制Chrome DevTools Protocol 检测区域里的CDP结果。y控制同一区域里的Dev Tool结果。- 顶部
CDP摘要项使用f || y。 - 任一结果为
true,总检测结果进入异常状态。
检测 1:CDP / Error.stack getter
还原后的核心逻辑:
js
function detectChromeDevToolsProtocol() {
let detected = false;
const errorObject = new window.Error();
window.Object.defineProperty(errorObject, "stack", {
configurable: false,
enumerable: false,
get: function () {
detected = true;
return "";
}
});
window.console.debug(errorObject);
return detected;
}
判定逻辑:
- 正常情况下,只创建
Error并传给console.debug,stackgetter 不一定会被同步读取。 - 某些 CDP/DevTools/自动化协议链路会为了展示或传输 console 参数而序列化
Error对象。 - 一旦序列化过程读取
error.stack,getter 触发,detected = true。 - 返回
true表示 CDP 相关序列化副作用被观察到。
这一条是同步探针,BrowserScan 直接把返回值写入 CDP 卡片。
检测 2:Dev Tool / Worker debugger timing
还原后的 Worker 代码:
js
onmessage = function() {
postMessage("before");
debugger;
postMessage("after");
};
主线程判定逻辑:
js
const blob = new Blob([workerCode], { type: "application/javascript" });
const url = URL.createObjectURL(blob);
const worker = new Worker(url);
let beforeAt;
worker.onmessage = function (event) {
if (event.data === "before") {
beforeAt = performance.now();
setTimeout(() => resolve(true), 500);
} else if (event.data === "after") {
const elapsedMs = performance.now() - beforeAt;
resolve(elapsedMs > 100);
}
};
worker.postMessage("");
判定逻辑:
- Worker 收到消息后先发
before。 - 随后执行
debugger。 - 如果没有调试器介入,Worker 很快继续执行并发出
after。 - 主线程计算
after - before,大于100ms判定异常。 - 如果
after在500ms内没有到达,也判定异常。
这一条是异步探针,BrowserScan 通过 yield Ve() 等待结果后写入 Dev Tool 卡片。
纯算边界
这里的"纯算"不能理解成脱离浏览器环境的固定公式。CDP 检测的输入不是字符串、canvas 图像或可枚举参数,而是运行时副作用:
console.debug是否触发Error.stackgetter,取决于控制台实现、DevTools/CDP 是否附着、自动化框架是否拦截 console 参数。debugger是否造成 Worker 暂停,取决于调试器状态、断点策略、Worker 调试支持和事件循环调度。- 因此 JS 代码可以纯复刻判定树,但最终真假必须在目标浏览器上下文里执行得到。
可稳定复刻的部分是:
- getter 设置方式;
- console 调用方式;
- Worker 源码;
100ms阈值;500ms兜底超时;CDP || Dev Tool的页面聚合逻辑。
产物说明
browserscan_bot_cdp_detection.js 暴露全局对象:
js
window.BrowserScanBotCDP
主要接口:
js
BrowserScanBotCDP.detectChromeDevToolsProtocol(window);
await BrowserScanBotCDP.detectDevToolDebuggerTiming(window);
await BrowserScanBotCDP.collect(window);
collect() 返回结构:
js
{
cdp: { name: "CDP", detected: false, ... },
devTool: { name: "Dev Tool", detected: false, elapsedMs: "<runtime number>", ... },
detected: false,
siteCompatible: {
cdp: false,
devTool: false,
abnormal: false
}
}
其中 siteCompatible.cdp 对齐页面 CDP 卡片,siteCompatible.devTool 对齐页面 Dev Tool 卡片。
验证结果
验证脚本:ruyipage_verify_browserscan_bot_cdp.py
验证方式:
powershell
node --check .\browserscan_bot_cdp_detection.js
python .\ruyipage_verify_browserscan_bot_cdp.py
实测页面 CDP 区域文本:
text
Chrome DevTools Protocol 检测 ... CDP 正常 Dev Tool 正常
本地复刻函数返回:
json
{
"cdp": false,
"devTool": false,
"abnormal": false
}
页面文本与复刻函数一致:CDP 为正常,Dev Tool 为正常。