方案:
我正在研究允许在Web工作环境中运行任意用户提供的代码的Javascript代码,类似于this approach。我们打电话给双方主持人(启动工人)和客人(工人内部)。
现在,我希望能够同时在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
中发送“停止”消息并隐藏scriptId
,使其在code
完成执行后立即执行。
This JSFiddle表明它有效,但也许我忽略了一些邪恶的边缘情况?
这意味着,我将case "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
消息,这将停止“超时计时器”!)。 / p>
答案 0 :(得分:1)
代码在任何执行环境中读取scriptId都不能吗?
是。代码只能访问其当前作用域(execution context)中的变量,而不能访问调用堆栈作用域中的局部变量。
但是,eval代码可以访问并可能覆盖/拦截全局postMessage
和onmessage
函数。如果您已经阻止它,他们仍然可以调用它们,因此您需要验证调用来自您的工作管理器而不是来自自定义代码。如果你已经确定了,一切都应该没问题。
请注意,当您在同一个"工作会话"中运行多个脚本时,您可能需要在每次运行后清理全局范围(或防止事先进行任何修改)。否则,独立脚本可能能够相互通信。