防范函数声明提升滥用的关键是切断可控输入参与函数定义的路径,禁止重声明/重赋值全局API,优先使用const函数表达式、严格模式、Object.freeze、运行时校验及CSP/SRI/Trusted Types等多层防护。

防范攻击者利用函数声明提升(hoisting)篡改内置行为,关键不在于阻止提升本身——这是JavaScript引擎固有机制,无法禁用——而在于切断其被滥用的路径。重点是避免将可控输入、动态拼接或外部代码直接参与函数定义过程,尤其警惕在全局或敏感作用域中混用声明与表达式。
避免在运行时动态覆盖内置或关键函数
攻击者常通过先声明同名函数再赋值的方式覆盖原有逻辑。例如:
危险写法:
function fetch() { /* 恶意重定义 */ }
fetch = function(url) { /* 实际执行恶意逻辑 */ };
此时即使原生 fetch 存在,也会被覆盖。应禁止对全局对象方法(如 fetch、XMLHttpRequest、console.log)进行任何形式的重声明或重赋值。
建议做法:
- 使用
Object.freeze(window)或Object.freeze(navigator)(需在早期执行,且注意兼容性)限制全局对象可写性 - 在模块化环境中,优先使用
import引入标准API,而非直接访问全局变量 - 对关键函数做存在性与类型校验:
if (typeof window.fetch !== 'function' || fetch.toString().includes('eval')) throw new Error('Suspicious override');
统一使用函数表达式并配合块级作用域
函数声明会提升并污染作用域顶部,而 const 声明的函数表达式不会被提升,且不可重复赋值。
推荐写法:
- 用
const myHandler = function() { ... };替代function myHandler() { ... } - 将敏感逻辑封装在 IIFE 或 ES 模块中,避免暴露到全局作用域
- 启用严格模式(
'use strict';),它禁止隐式创建全局变量,也限制部分危险操作
运行时校验与沙箱隔离
仅靠语法约束不够,需结合运行时防护:
- 在初始化阶段快照关键函数的
toString()和name属性,后续定期比对是否被篡改 - 对高风险操作(如发起网络请求、读取 localStorage)封装为受控代理函数,内部校验调用栈与上下文
- 在 CSP(Content Security Policy)中启用
script-src 'self'并禁用unsafe-eval,从源头阻断动态代码执行
前端防篡改加固实践
针对 Webshell 或恶意脚本注入场景,需叠加多层防御:
- 服务端输出 HTML 时,对内联
<script>标签做白名单校验,拒绝含function声明、eval、new Function的脚本块 - 前端加载 JS 前,用 Subresource Integrity(SRI)校验完整性,防止 CDN 被劫持篡改
- 关键业务页面启用 Trusted Types API,强制所有动态脚本创建必须经由安全策略接口

















