Python Selenium Chrome反bot检测 CHR_MEMORY等项失败解决
问题背景
基于Python开发的Selenium Chrome驱动在两个公开Bot检测站点存在测试项不通过的情况:
- 访问bot.sannysoft.com时,
CHR_MEMORY检测项结果为FAIL - 访问bot.incolumitas.com时,两项检测失败:
inconsistentWebWorkerNavigatorPropery: FAIL、inconsistentServiceWorkerNavigatorPropery: FAIL
已尝试Stack Overflow上多数高赞方案,包括修改navigator.webdriver标识规避Selenium检测、手动设置deviceMemory值、伪造navigator全局属性等,均未解决上述三个失败项,其余检测项已全部通过。
针对CHR_MEMORY检测失败,已尝试两种实现方案:
- 页面加载后执行JS注入修改属性:
driver.execute_script("Object.defineProperty(navigator, 'deviceMemory', {get: () => 8})")
- 通过CDP命令在文档创建前注入属性修改脚本:
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": """ Object.defineProperty(navigator, 'deviceMemory', { get: () => Number(2) }); """ })
上述两种方案执行后,Fp-collect采集到的deviceMemory值确实显示为设置的数值,但仍未通过CHR_MEMORY检测。
核心疑问
WebWorkerNavigatorPropery、ServiceWorkerNavigatorPropery两个检测项的具体含义是什么?- 如何绕过上述两项以及CHR_MEMORY的Bot检测?
检测结果参考
- sannysoft检测结果截图:

- incolumitas检测结果截图:

问题解答
两个失败检测项含义
- inconsistentWebWorkerNavigatorPropery:校验主线程window上下文的navigator属性,与Web Worker独立线程上下文下的navigator属性是否一致。原生Chrome环境中两个上下文的同源navigator属性值完全匹配;若仅修改主线程navigator属性、未同步覆盖Web Worker上下文的对应属性,会直接触发该检测项失败。
- inconsistentServiceWorkerNavigatorPropery:检测逻辑与前者一致,对比维度为主线程navigator属性与Service Worker线程上下文的navigator属性值,仅修改主线程属性会触发检测失败。
各检测项绕过方案
1. CHR_MEMORY检测绕过
原有方案失效核心原因:CHR_MEMORY并非仅读取主线程navigator.deviceMemory值做判断,而是会交叉校验Chrome内存JSBI接口、Performance内存API等多维度返回的内存数据,单一修改navigator.deviceMemory无法通过交叉校验。
正确实现逻辑:通过CDP的Page.addScriptToEvaluateOnNewDocument接口在文档创建最早期注入脚本,同时覆盖所有内存相关读取入口,且设置的内存值需符合Chrome真实取值规则(仅支持0.25、0.5、1、2、4、8、16、32这类2的幂次固定值,不可自定义随机数值),参考注入代码:
Object.defineProperty(navigator, 'deviceMemory', { get: () => 8, configurable: true }); // 同步覆盖performance内存接口返回值 if (window.performance && performance.memory) { Object.defineProperty(performance.memory, 'jsHeapSizeLimit', { get: () => 8 * 1024 * 1024 * 1024 * 0.9 // 8G物理内存对应堆内存上限约7.2G }); Object.defineProperty(performance.memory, 'totalJSHeapSize', { get: () => 8 * 1024 * 1024 * 1024 * 0.2 }); }
禁止在页面onload阶段执行属性注入,避免页面前置脚本读取到原始属性值。
2. WebWorker/ServiceWorker navigator属性不一致检测绕过
原有注入脚本仅作用于主线程上下文,未覆盖Worker独立线程,导致属性值不匹配。需在CDP初始注入脚本中重写Worker、ServiceWorker的创建逻辑,在Worker初始化阶段同步注入相同的属性修改代码,保证三个上下文的navigator属性完全一致,参考实现:
// 重写Worker构造函数,同步注入属性修改逻辑 const nativeWorker = window.Worker; window.Worker = function(url, options) { const workerIns = new nativeWorker(url, options); const injectCode = ` Object.defineProperty(self.navigator, 'deviceMemory', {get: () => 8}); // 此处需同步补充所有主线程已修改的navigator属性,包括webdriver、hardwareConcurrency、platform、userAgent等 `; // 向Worker注入代码执行 workerIns.postMessage({__runCode: injectCode}); workerIns.addEventListener('message', (e) => { if (e.data && e.data.__runCode) { eval(e.data.__runCode); } }) return workerIns; } // 重写ServiceWorker注册逻辑,改写脚本内容后再注册 const nativeSwRegister = window.navigator.serviceWorker.register; window.navigator.serviceWorker.register = async function(scriptUrl, options) { const scriptRes = await fetch(scriptUrl); let scriptContent = await scriptRes.text(); // 前置注入属性修改代码 scriptContent = ` Object.defineProperty(self.navigator, 'deviceMemory', {get: () => 8}); // 此处需同步补充所有主线程已修改的navigator属性,与Worker、主线程配置完全一致 ` + scriptContent; const scriptBlob = new Blob([scriptContent], {type: 'application/javascript'}); const blobUrl = URL.createObjectURL(scriptBlob); return nativeSwRegister.call(this, blobUrl, options); }
注意:所有主线程中修改过的navigator属性,必须在Web Worker、ServiceWorker上下文做完全相同的赋值,避免出现任何属性值差异。
内容的提问来源于stack exchange,提问作者MrBrendan

