如何阻止Error对象的stack属性被篡改及拦截Tampermonkey脚本?
一、阻止Error对象的stack属性被篡改
要保护Error的stack不被第三方脚本篡改,核心是锁定stack属性的可写性,或者在错误实例创建时就冻结它,让后续代码无法修改。
方案1:冻结Error实例
重写全局Error构造函数,创建错误实例后立即冻结,阻止任何属性修改:
// 保存原生Error构造函数 const OriginalError = window.Error; window.Error = function(...args) { const err = new OriginalError(...args); // 冻结实例,所有属性变为只读、不可配置 Object.freeze(err); return err; }; // 同理覆盖其他错误类型(如TypeError、ReferenceError) const errorTypes = ['TypeError', 'ReferenceError', 'RangeError', 'SyntaxError']; errorTypes.forEach(type => { window[`Original${type}`] = window[type]; window[type] = function(...args) { const err = new window[`Original${type}`](...args); Object.freeze(err); return err; }; });
这种方式下,任何试图修改err.stack的操作都会直接失败(严格模式下抛出错误,非严格模式下静默失效)。
方案2:锁定Error.prototype的stack属性
通过Object.defineProperty把Error.prototype.stack设为不可写、不可配置,从原型层面阻止篡改:
// 获取原生stack属性的描述符 const originalStackDesc = Object.getOwnPropertyDescriptor(Error.prototype, 'stack'); // 重新定义为不可写、不可配置 Object.defineProperty(Error.prototype, 'stack', { ...originalStackDesc, writable: false, configurable: false });
这个方案能阻止第三方脚本通过err.stack = 'xxx'或者重新定义stack属性来篡改,但需要确保你的代码在Tampermonkey脚本之前执行。
二、阻止Tampermonkey脚本加载或运行的实用方法
1. 拦截脚本注入(DOM层面)
Tampermonkey会向页面插入script标签,我们可以在DOM节点插入的捕获阶段拦截并移除这些脚本:
document.addEventListener('DOMNodeInserted', (e) => { const target = e.target; if (target.tagName !== 'SCRIPT') return; // 识别Tampermonkey脚本:要么包含用户脚本头,要么来源是Tampermonkey的内部URL if (target.textContent.includes('// ==UserScript==') || target.src.includes('tampermonkey')) { target.remove(); } }, true); // 使用捕获阶段,确保在脚本执行前拦截
2. 禁用Tampermonkey核心API
Tampermonkey会在全局暴露GM_*系列API(如GM_xmlhttpRequest、GM_addStyle),提前覆盖这些API让它们失效:
// 遍历并覆盖所有GM_开头的函数 Object.keys(window).forEach(key => { if (key.startsWith('GM_')) { window[key] = function() { throw new Error('Tampermonkey API is blocked'); }; // 锁定属性,防止被重新赋值 Object.defineProperty(window, key, { writable: false, configurable: false }); } });
3. 利用Content Security Policy(CSP)
如果是你自己的网站,在HTTP响应头设置严格的CSP规则,禁止加载第三方脚本:
Content-Security-Policy: script-src 'self' https://your-trusted-cdn.com; object-src 'none'; base-uri 'self';
这种方式能从根源上阻止Tampermonkey注入的脚本(无论是inline还是外部源)执行,但需要服务器端配合配置。
4. 检测并终止第三方脚本执行
通过错误栈检测是否有Tampermonkey脚本在运行,一旦检测到就抛出错误终止执行:
function blockTampermonkey() { try { throw new Error(); } catch (err) { const stack = err.stack; // 匹配Tampermonkey相关的栈信息 if (stack.includes('tampermonkey') || stack.includes('userscript')) { throw new Error('Unauthorized script execution blocked'); } } } // 在关键业务函数前调用,或者定时检测 setInterval(blockTampermonkey, 100);
注意:所有上述方案的核心前提是你的代码要比Tampermonkey脚本更早执行——如果是自己的网站,把防护脚本放在<head>的最顶部;如果是前端代码,确保脚本注入时机为document-start。
内容的提问来源于stack exchange,提问作者some dumb guy

