如何在WebView中永久覆盖JS DOM API并拦截指定JS函数防篡改?
嘿,这个问题问到点子上了——我刚好在做类似的浏览器安全拦截功能,给你梳理下可行的方案和注意点:
delete移除? 默认情况下,是的。window上的原生属性(比如fetch、XMLHttpRequest、postMessage)默认的configurable特性是true,这意味着它们可以被删除、重新配置。举个例子:
// 你先覆盖fetch window.fetch = (...args) => { /* 你的拦截逻辑 */ }; // 后续代码可以直接删除 delete window.fetch; // 此时fetch会恢复成原生版本
所以直接覆盖肯定不安全,得用更稳妥的方式锁定这些属性。
Object.defineProperty锁定拦截后的属性 这是JS层面最常用的可靠方法——通过重新定义属性,把configurable和writable都设为false,这样既不能删除,也不能重新赋值。
拦截fetch的示例:
const originalFetch = window.fetch; Object.defineProperty(window, 'fetch', { value: function(...args) { // 这里加你的拦截判断:比如检查请求域名、是否来自目标iframe const [url] = args; if (url.includes('https://blocked-domain.com')) { console.log('Fetch请求被拦截'); return Promise.reject(new Error('Blocked by custom browser')); } // 符合条件就调用原生fetch return originalFetch.apply(this, args); }, writable: false, // 禁止重新赋值 configurable: false, // 禁止删除/重新配置 enumerable: true // 保持原属性的可枚举性 });
拦截XMLHttpRequest的示例:
XHR是构造函数,我们可以拦截它的open方法来检查请求:
const originalXHR = XMLHttpRequest; Object.defineProperty(window, 'XMLHttpRequest', { value: function() { const xhr = new originalXHR(); // 拦截open方法 const originalOpen = xhr.open; xhr.open = function(method, url) { if (url.includes('https://blocked-domain.com')) { throw new Error('XHR请求被拦截'); } return originalOpen.apply(this, arguments); }; return xhr; }, writable: false, configurable: false });
拦截postMessage的示例:
同样锁定window.postMessage,检查消息目标或来源:
const originalPostMessage = window.postMessage; Object.defineProperty(window, 'postMessage', { value: function(message, targetOrigin, transfer) { // 拦截逻辑:比如检查目标域名是否是要阻止的 if (targetOrigin === 'https://blocked-domain.com') { console.log('postMessage被拦截'); return; } return originalPostMessage.apply(this, arguments); }, writable: false, configurable: false });
设置完之后,delete window.fetch会返回false(严格模式下直接抛错),也无法重新赋值这些属性。
Object.freeze/Object.seal强化锁定 如果想更彻底,可以用Object.freeze冻结拦截后的属性——它会把属性设为不可写、不可配置、不可枚举(保持原枚举性的话还是用defineProperty更灵活)。不过注意,冻结的是属性值本身,比如你覆盖的函数对象,冻结后它的内部属性也不能修改:
// 先覆盖,再冻结 window.fetch = (...args) => { /* 拦截逻辑 */ }; Object.freeze(window.fetch);
或者用Object.seal,它允许修改属性值但禁止删除和重新配置,适合需要后续调整拦截逻辑的场景。
JS层面的拦截始终存在被绕过的可能(比如页面提前备份了原生对象),所以如果是开发自定义浏览器(比如安卓Chrome衍生版、WebView应用),原生层面的拦截才是终极方案。
安卓Chrome/WebView的实现方式:
拦截网络请求(fetch/XHR):
用WebViewClient的shouldInterceptRequest方法,能拦截所有网络请求,直接在原生层拒绝目标域名的请求:webView.setWebViewClient(new WebViewClient() { @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { String url = request.getUrl().toString(); if (url.contains("blocked-domain.com")) { // 返回空响应,阻止请求 return new WebResourceResponse(null, null, null); } return super.shouldInterceptRequest(view, request); } });拦截
postMessage:
可以通过addJavascriptInterface注入原生检查逻辑,或者在页面加载最早期(onPageStarted时机)注入你的JS拦截代码,确保在页面所有JS执行前生效。如果是浏览器扩展,还可以用chrome.webRequestAPI拦截,权限更高更稳定。
- 注入时机要早:不管是JS还是原生注入,一定要在页面的所有JS代码执行前加载你的拦截逻辑,否则页面可能已经备份了原生对象,绕过你的拦截。
- iframe要覆盖到:如果要拦截特定iframe,确保你的拦截代码能注入到所有iframe中(比如浏览器扩展设置
all_frames: true,WebView开启跨域权限并注入代码到iframe)。
内容的提问来源于stack exchange,提问作者Gregory Magarshak

