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

如何安全执行含预定义对象与函数的用户JavaScript代码?

解决方案:安全且非阻塞的用户脚本执行方案

针对你的移动应用构建器面临的主线程阻塞和Web端恶意脚本风险问题,结合无法向隔离线程传递函数的限制,推荐采用消息代理模式分平台实现:

核心逻辑

由于postMessage仅支持可序列化数据,我们不在隔离线程中直接持有showAlert、haptics这类原生对象/函数,而是在隔离环境中创建代理对象/函数——这些代理通过消息向主线程发送执行指令,主线程完成实际操作后将结果回传,以此绕过函数传递的限制,同时实现权限管控。


React Native 端:Worklets + 消息回调

Worklets运行在UI线程,天然不会阻塞JS主线程,完美解决第一个问题:

  1. 在Worklet中定义代理函数
    为每个预定义函数创建Worklet内的代理,通过sendToJS向主线程发送指令:

    // Worklet内部的代理函数
    const showAlert = (title: string | undefined, message: string, actions: AlertAction[]) => {
      'worklet';
      sendToJS({ 
        type: 'invoke', 
        funcName: 'showAlert', 
        payload: { title, message, actions } 
      });
    };
    
    // 只读对象直接序列化传入(无安全风险)
    const currentUser = JSON.parse($args.currentUser);
    
  2. 主线程监听并执行实际逻辑
    主线程接收Worklet的消息,调用原生实现并处理结果:

    // 主线程启动Worklet
    const userScriptWorklet = runOnUI((serializedArgs) => {
      'worklet';
      const args = JSON.parse(serializedArgs);
      // 这里执行用户的脚本逻辑,调用的是代理函数
      eval(args.formulaString);
    })(JSON.stringify({
      formulaString: userInputCode,
      currentUser: JSON.stringify(this.currentUser)
      // 其他只读数据同理
    }));
    
    // 监听Worklet的指令
    userScriptWorklet.addListener((event) => {
      if (event.type === 'invoke') {
        const { funcName, payload } = event;
        switch(funcName) {
          case 'showAlert':
            this.showAlert(payload.title, payload.message, payload.actions);
            break;
          case 'openPage':
            this.openPage(payload.pageId);
            break;
          // 其他预定义函数逐一映射
        }
      }
    });
    

Web 端:Web Worker/Iframe 沙箱 + 消息代理

Web Worker解决主线程阻塞问题,配合iframe沙箱彻底隔离恶意脚本的访问权限:

1. 沙箱隔离配置

  • 若用Web Worker:Worker本身与主线程隔离,默认无法访问DOM、Cookie等敏感数据,只需避免暴露全局对象。
  • 若用Iframe:设置严格的沙箱属性,彻底阻断恶意脚本的跨域/权限访问:
    <iframe id="scriptSandbox" sandbox="allow-scripts" style="display: none;"></iframe>
    
    不要添加allow-same-origin,防止脚本读取父页面的Cookie、localStorage。

2. 代理函数与消息交互

在隔离环境(Worker/Iframe)中预定义代理,通过postMessage与主线程通信:

// Worker/Iframe内的全局代理实现
self.showAlert = async (title, message, actions) => {
  return new Promise((resolve) => {
    const reqId = Math.random().toString(36).slice(2);
    // 发送执行请求
    self.postMessage({
      type: 'invoke',
      id: reqId,
      funcName: 'showAlert',
      payload: { title, message, actions }
    });
    // 监听结果回传
    const handler = (e) => {
      if (e.data.id === reqId) {
        resolve(e.data.result);
        self.removeEventListener('message', handler);
      }
    };
    self.addEventListener('message', handler);
  });
};

// 接收主线程传入的只读数据
self.addEventListener('message', (e) => {
  if (e.data.type === 'init') {
    self.currentUser = JSON.parse(e.data.payload.currentUser);
    // 初始化完成后执行用户脚本
    eval(e.data.payload.formulaString);
  }
});

3. 主线程消息处理

主线程仅响应预定义的函数调用,严格控制权限:

// 主线程初始化隔离环境并监听消息
const worker = new Worker('script-sandbox.js');
// 或获取iframe的contentWindow
// const worker = document.getElementById('scriptSandbox').contentWindow;

worker.postMessage({
  type: 'init',
  payload: {
    formulaString: userInputCode,
    currentUser: JSON.stringify(currentUser)
  }
});

worker.addEventListener('message', async (e) => {
  if (e.data.type === 'invoke') {
    const { id, funcName, payload } = e.data;
    let result;
    // 仅允许预定义的函数调用
    switch(funcName) {
      case 'showAlert':
        result = await showAlert(payload.title, payload.message, payload.actions);
        break;
      case 'shareSheet':
        result = await shareSheet.open(payload.content);
        break;
      // 拒绝未授权的函数调用
      default:
        result = new Error('Unauthorized function call');
    }
    // 回传执行结果
    worker.postMessage({ type: 'result', id, result });
  }
});

代码补全兼容性保障

确保隔离环境中的代理函数/对象结构与Monaco Editor中预定义的接口完全一致,比如Monaco中声明的showAlert参数、返回值,要和代理函数的定义严格匹配,这样用户输入的TS代码在转译后能无缝适配代理逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 05:33:14