Javascript 是否可以读取父堆栈帧的局部变量?

Javascript 是否可以读取父堆栈帧的局部变量?,javascript,html,security,web-worker,Javascript,Html,Security,Web Worker,场景: 我正在编写Javascript代码,该代码允许在web worker环境中运行任意用户提供的代码,类似于。让我们调用双方主机(启动worker)和来宾(在worker内部) 现在,我希望能够同时在worker上运行多个脚本,或者至少立即将它们发送给worker,而不必等待上一个脚本完成。减少延迟和开销是至关重要的,因为单个“会话”可能需要数百万甚至数十亿次脚本运行——我在几秒钟内做得越多越好;是的,我选择Javascript有一个很好的理由(性能很重要,但不是最重要的方面) 但是,这意味

场景

我正在编写Javascript代码,该代码允许在web worker环境中运行任意用户提供的代码,类似于。让我们调用双方主机(启动worker)和来宾(在worker内部)

现在,我希望能够同时在worker上运行多个脚本,或者至少立即将它们发送给worker,而不必等待上一个脚本完成。减少延迟和开销是至关重要的,因为单个“会话”可能需要数百万甚至数十亿次脚本运行——我在几秒钟内做得越多越好;是的,我选择Javascript有一个很好的理由(性能很重要,但不是最重要的方面)

但是,这意味着工作人员需要可靠地告诉我脚本何时启动和停止。这需要发送一些消息,包括一个唯一的脚本标识符

代码

我目前正在来宾系统中使用此代码:

onmessage = function(event) {
    var scriptId = event.data.id;
    var cmd = event.data.cmd;
    var args = event.data.args;

    switch (cmd) {
        case "init":
            // code here to lock down the worker and disable everything potentially insecure, except for postMessage
            break;
        case "run":
            if (!workerInitialized()) return;
            // run user given code
            var code = args.code;
            // ...
            postMessage({cmd: "start", args: scriptId});        // signal that script started
            runScript(code);        // runs eval(code) with proper error reporting etc.
            postMessage({cmd: "stop", args: scriptId});         // signal that script stopped
            break;
        // ...
    }
}
问题

如您所见,
scriptId
存在于
eval(code)
上方的堆栈框架中。我正在使用一些加密库来生成id,这样用户就不可能猜到id(因为工作人员在任何错误的猜测中都会被杀死)。另外,
onmessage
不能被
code
覆盖

当代码在
code
作者无法控制的环境中执行时(例如,在节点服务器上或在其他用户的浏览器中),我是否可以确保
code
不能“伪造”停止消息

可能的解决方案

setTimeout
中发送“stop”消息,并在closure中隐藏
scriptId
,以便在
code
完成执行后立即执行

这表明它是有效的,但也许我忽略了一些邪恶的边缘案例

这意味着,我将
案例“run”
代码更改为:

postMessage({cmd: "start", args: scriptId});        // signal that script started
setTimeout((function(scriptId) { return function() { postMessage({cmd: "stop", args: scriptId}); };})(scriptId), 0);         // signal that script stopped, right after this function returned
scriptId = null;
runScript(code);        // runs eval(code) with proper error reporting etc.
在任何执行环境中,
code
都不可能读取
scriptId
?我甚至不在乎它是否能停止计时器,因为工人无论如何都会在固定超时后被杀死(只要它不能伪造将停止“超时计时器”的
stop
消息!)

在任何执行环境中,代码都不能读取scriptId吗

对。代码只能访问(的)当前作用域中的变量,而不能访问调用堆栈上作用域中的局部变量

但是,评估代码可以访问并可能覆盖/截获全局
postMessage
onmessage
函数。如果您阻止了这一点,他们仍然可以调用它们,因此您需要验证调用是否来自您的worker manager,而不是来自自定义代码。如果你保证了这一点,一切都会好起来的


请注意,当您在同一个“工作会话”中运行多个脚本时,您可能需要在每次运行后清理全局作用域(或防止事先进行任何修改)。否则,独立的脚本可能能够相互通信。

因此我确实采取了一些预防措施:
onmessage
postMessage
不能使用。但是,用户代码可以调用
postMessage
,这就是为什么它不能伪造
scriptId
:我使用“stop”消息来确定脚本实际上已停止(并且我不必在超时后杀死worker)。如果可以伪造,工作超时计时器将停止,脚本可以继续运行,这是错误的。那么,ECMA标准说它不可能得到父stackframe变量?仅供参考:上下文不需要清理,因为它是一个“会话”(仅涉及单个作者的代码)。呃,ECMA标准不允许引入某种内省API;但是它明确指定普通变量名解析只检查当前执行上下文(堆栈帧)的父范围,而不搜索整个堆栈。如果发送了
stop
消息,您仍然可以杀死工作进程,不是吗?顺便说一句,您是否禁止使用
setTimeout
和其他异步函数,以便在脚本将控制权返回给您后不会“停止”脚本?谢谢您的帮助!是的,默认情况下,
setTimeout
setInterval
是不允许的,我只是在考虑我的选择。所以基本上,只要用户脚本不能访问调试器API,一切都好吗?因为,我是清白的,还是我在监督什么?