Firefox扩展开发:如何拦截eval、innerHTML等函数调用?
解决Firefox扩展拦截eval、innerHTML调用的方案
一、通过exportFunction替换原函数/属性
Firefox扩展的Content Script可以利用exportFunction API将自定义逻辑注入到页面上下文,绕过直接修改原生属性的限制:
1. 拦截eval
// 在Content Script中执行 // 获取页面上下文的原生eval const originalEval = window.wrappedJSObject.eval; // 自定义拦截逻辑 function interceptedEval(...args) { // 这里添加你的处理:记录调用、校验参数、阻止执行等 console.log("拦截到eval调用,参数:", args); // 若需放行,调用原生eval: // return originalEval.apply(window.wrappedJSObject, args); throw new Error("eval调用已被拦截"); } // 将拦截函数注入页面上下文,替换原生eval exportFunction(interceptedEval, window.wrappedJSObject, { defineAs: "eval" });
2. 拦截innerHTML
针对元素的innerHTML属性,需要修改Element.prototype的属性描述符,结合exportFunction注入自定义的getter/setter:
// 获取页面上下文Element.prototype的原生innerHTML描述符 const originalInnerHTMLDesc = Object.getOwnPropertyDescriptor( window.wrappedJSObject.Element.prototype, "innerHTML" ); // 自定义getter function interceptedGetInnerHTML() { console.log("获取innerHTML,目标元素:", this); return originalInnerHTMLDesc.get.call(this); } // 自定义setter function interceptedSetInnerHTML(value) { console.log("设置innerHTML,内容:", value, "目标元素:", this); // 若需放行,调用原生setter: // originalInnerHTMLDesc.set.call(this, value); throw new Error("innerHTML设置已被拦截"); } // 构造新的属性描述符并注入页面上下文 const newInnerHTMLDesc = { get: exportFunction( interceptedGetInnerHTML, window.wrappedJSObject.Element.prototype ), set: exportFunction( interceptedSetInnerHTML, window.wrappedJSObject.Element.prototype ), configurable: true, enumerable: originalInnerHTMLDesc.enumerable }; Object.defineProperty( window.wrappedJSObject.Element.prototype, "innerHTML", newInnerHTMLDesc );
二、使用Proxy包装全局对象/元素原型
通过Proxy代理页面上下文的全局对象或元素原型,拦截属性访问与调用:
// 在Content Script中执行 const pageWindow = window.wrappedJSObject; // 代理全局window对象,拦截eval const proxiedWindow = new Proxy(pageWindow, { get(target, prop) { if (prop === "eval") { return function(...args) { console.log("拦截到eval调用:", args); // 放行逻辑:return target.eval(...args); throw new Error("eval调用已被拦截"); }; } return Reflect.get(target, prop); } }); // 将代理后的window覆盖原页面的window对象 Object.defineProperty(pageWindow, "window", { value: proxiedWindow, configurable: true, writable: true }); // 代理Element.prototype,拦截innerHTML const proxiedElementProto = new Proxy(pageWindow.Element.prototype, { get(target, prop) { if (prop === "innerHTML") { return new Proxy(target[prop], { get(obj) { console.log("获取innerHTML:", obj); return obj; }, set(obj, value) { console.log("设置innerHTML:", value); // 放行逻辑:target.innerHTML = value; return true; } }); } return Reflect.get(target, prop); } }); // 覆盖原Element.prototype Object.defineProperty(pageWindow, "Element", { value: { prototype: proxiedElementProto }, configurable: true, writable: true });
注意事项
- 若页面使用
Function构造函数模拟eval(如new Function(code)),需额外拦截Function构造函数。 - 类似
insertAdjacentHTML这类可插入HTML的方法,也需要按相同逻辑拦截。 - Manifest V3中需确保Content Script的匹配规则覆盖目标页面,如设置
"all_frames": true、"match_about_blank": true等。 - 所有注入到页面上下文的函数/对象,必须使用
exportFunction或cloneInto处理,避免跨上下文安全报错。
内容的提问来源于stack exchange,提问作者Юрий Стражнов
相关产品推荐
相关产品推荐

