You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

重构JavaScript动态加载脚本以避免eval,提升程序安全性

安全动态加载用户脚本并获取类实例的解决方案

你的核心问题有两个:一是window['myclass']获取失败,本质是脚本异步加载的时机没处理好;二是eval的安全风险,需要用更可控的方式访问类。下面是几个可落地的安全方案:

方案一:改进异步加载时机+专用命名空间注册表

1. 封装脚本加载为Promise(确保加载完成再执行后续逻辑)

原来的loadNewScript没有处理异步加载的完成事件,导致你调用loadObject时脚本可能还没加载完毕,自然找不到window['myclass']。先把加载逻辑改成Promise形式:

function loadNewScript(scriptName) {
  return new Promise((resolve, reject) => {
    const script = document.createElement("script");
    script.type = "text/javascript";
    script.src = `/static/techniques/${scriptName}`;
    
    // 加载成功/失败的回调
    script.onload = () => resolve();
    script.onerror = (error) => reject(new Error(`脚本加载失败: ${scriptName}`));
    
    // 直接挂载到body,比原来的节点查找更可靠
    document.body.appendChild(script);
  });
}

2. 给用户脚本指定专用注册空间

避免污染全局window,同时让类的获取更可控。先在前端初始化一个专用注册表:

// 全局初始化(比如应用启动时执行一次)
window.CustomScriptRegistry = {};

然后要求用户脚本(已审核)修改格式,将类注册到这个表中:

// myclass.js 示例
const myclass = class {
  // 你的类实现
};

// 注册到专用命名空间
window.CustomScriptRegistry['myclass'] = myclass;

3. 安全获取类实例

现在可以安全地从注册表中获取类,同时确保脚本已加载完成:

async function loadObject(technique) {
  const className = technique.split(".")[0];
  try {
    // 等待脚本加载完成
    await loadNewScript(technique);
    
    // 从专用注册表获取类,避免全局遍历
    const TargetClass = window.CustomScriptRegistry[className];
    if (typeof TargetClass === 'function') {
      return new TargetClass();
    }
    
    return null;
  } catch (error) {
    console.error('实例化脚本类失败:', error);
    return null;
  }
}

方案二:使用ES模块动态加载(更现代、安全的方式)

如果你的环境支持ES模块(现代浏览器都支持),可以彻底抛弃全局变量,用模块化方式加载:

1. 修改用户脚本为ES模块

// myclass.js 改为模块形式
export default class myclass {
  // 你的类实现
}

2. 动态导入模块并实例化

直接用import()函数异步加载模块,无需全局变量:

async function loadObject(technique) {
  try {
    // 动态导入模块
    const module = await import(`/static/techniques/${technique}`);
    const TargetClass = module.default;
    
    if (typeof TargetClass === 'function') {
      return new TargetClass();
    }
    return null;
  } catch (error) {
    console.error('加载模块或实例化失败:', error);
    return null;
  }
}

注意:使用ES模块需要确保服务器正确设置响应头(比如Content-Type: application/javascript),部分服务器可能需要将模块文件后缀改为.mjs,或者配置支持.js作为模块。

额外安全建议

  • 严格控制脚本目录:Flask侧要确保只能访问指定的脚本目录,防止路径遍历攻击(比如校验scriptName不包含../等恶意路径)。
  • 保留审核机制:即使使用了安全加载方式,人工审核和单元测试仍是第一道防线,重点检查脚本是否包含危险API调用(如eval、Function构造器、DOM操作权限等)。
  • 限制脚本权限:可以考虑用沙箱环境(如iframe沙箱)运行用户脚本,进一步隔离风险,但如果你的绘图应用需要脚本直接操作主应用DOM,这个方案需要额外适配。

内容的提问来源于stack exchange,提问作者erik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 19:15:12