Javascript 是否可以读取父堆栈帧的局部变量?
场景: 我正在编写Javascript代码,该代码允许在web worker环境中运行任意用户提供的代码,类似于。让我们调用双方主机(启动worker)和来宾(在worker内部) 现在,我希望能够同时在worker上运行多个脚本,或者至少立即将它们发送给worker,而不必等待上一个脚本完成。减少延迟和开销是至关重要的,因为单个“会话”可能需要数百万甚至数十亿次脚本运行——我在几秒钟内做得越多越好;是的,我选择Javascript有一个很好的理由(性能很重要,但不是最重要的方面) 但是,这意味着工作人员需要可靠地告诉我脚本何时启动和停止。这需要发送一些消息,包括一个唯一的脚本标识符 代码: 我目前正在来宾系统中使用此代码:Javascript 是否可以读取父堆栈帧的局部变量?,javascript,html,security,web-worker,Javascript,Html,Security,Web Worker,场景: 我正在编写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,一切都好吗?因为,我是清白的,还是我在监督什么?