Chrome扩展中拦截fetch()无法捕获全部请求的问题求助
Chrome扩展Fetch拦截漏请求的排查与修复方案
可能的漏请求原因
- 页面预加载脚本提前捕获了原始
fetch引用:部分页面会通过预加载机制,在你的注入脚本执行前就保存了fetch的原始引用,后续调用时直接使用该引用绕过重写逻辑。 fetch重写实现存在漏洞:未覆盖所有调用场景(如this绑定、非标准参数传递),或未正确代理返回的Promise,导致部分请求未被拦截。- 跨帧消息传递丢失:
postMessage可能被页面安全策略拦截,或消息监听逻辑存在遗漏,导致请求信息未被接收。 - 动态加载脚本未被覆盖:页面通过
eval、动态<script>标签加载的脚本,可能直接引用原始fetch,未使用重写后的版本。
针对性修复方案
1. 确保注入脚本优先执行
除了在manifest中设置"run_at": "document_start"和"all_frames": true,还需利用世界隔离特性将脚本注入到页面脚本所在的MAIN世界,确保重写的fetch覆盖所有页面脚本:
- Manifest V3 配置示例:
"content_scripts": [ { "matches": ["<all_urls>"], "js": ["content_script.js"], "run_at": "document_start", "all_frames": true, "world": "MAIN" } ]
- 注入脚本时设置同步加载,避免被页面脚本插队:
// content_script.js 中注入inject.js的逻辑 function injectScript() { const script = document.createElement('script'); script.src = chrome.runtime.getURL('inject.js'); script.async = false; // 同步执行,确保优先覆盖fetch document.documentElement.appendChild(script); script.remove(); } injectScript();
2. 正确重写fetch方法
重写时需完全代理原始方法,处理this绑定、所有参数、返回的Promise,并克隆响应避免流被消耗:
// inject.js const originalFetch = window.fetch; window.fetch = async function(...args) { // 捕获请求信息 const requestInfo = args[0]; const requestInit = args[1] || {}; const requestDetails = { url: typeof requestInfo === 'string' ? requestInfo : requestInfo.url, method: requestInit.method || 'GET', headers: Object.fromEntries(requestInit.headers || new Headers()), body: requestInit.body ? await new Response(requestInit.body).text() : null }; // 发送请求信息到content_script window.postMessage({ type: 'FETCH_REQUEST', data: requestDetails }, '*'); // 调用原始fetch并处理响应 const response = await originalFetch.apply(this, args); const clonedResponse = response.clone(); // 克隆响应避免流被消耗 const responseDetails = { url: response.url, status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers), body: await clonedResponse.text() }; // 发送响应信息 window.postMessage({ type: 'FETCH_RESPONSE', data: responseDetails }, '*'); return response; };
3. 加固消息监听逻辑
在content_script.js中验证消息来源,避免遗漏或接收恶意消息:
// content_script.js window.addEventListener('message', (event) => { // 仅接收当前页面的消息 if (event.source !== window) return; switch(event.data.type) { case 'FETCH_REQUEST': console.log('捕获fetch请求:', event.data.data); // 可转发到background.js做后续处理 chrome.runtime.sendMessage({ type: 'LOG_FETCH_REQUEST', data: event.data.data }); break; case 'FETCH_RESPONSE': console.log('捕获fetch响应:', event.data.data); break; } });
4. 补充浏览器层面拦截(推荐)
如果重写fetch仍有遗漏,可结合Chrome扩展的declarativeNetRequest API,从浏览器层面拦截所有网络请求:
- Manifest V3 配置示例:
"permissions": ["declarativeNetRequest"], "host_permissions": ["<all_urls>"], "declarative_net_request": { "rule_resources": [ { "id": "fetch_intercept_rules", "enabled": true, "path": "rules.json" } ] }
rules.json示例(拦截并记录所有fetch/XHR请求):
[ { "id": 1, "priority": 1, "action": { "type": "log" }, "condition": { "urlFilter": "*", "resourceTypes": ["xmlhttprequest", "fetch"] } } ]
内容的提问来源于stack exchange,提问作者surya
相关产品推荐
相关产品推荐

