简体   繁体   English

如何在浏览器中安全地运行用户提供的 Javascript 代码?

[英]How to safely run user-supplied Javascript code inside the browser?

Imagine a scenario where I want to continuously invoke user-supplied Javascript code, like in the following example, where getUserResult is a function that some user (not myself) has written:想象一个场景,我想不断调用用户提供的 Javascript 代码,如下例所示,其中getUserResult是某个用户(不是我自己)编写的函数:

for (var i = 0; i < N; ++i) {
    var x = getUserResult(currentState);
    updateState(currentState, x);
}

How can I execute that kind of code in a browser and/or Node.js, without any security risks?如何在浏览器和/或 Node.js 中执行这种代码,而没有任何安全风险?

More generally, how can I execute a Javascript function that is not allowed to modify or even read the current webpage or any other global state?更一般地说,我如何执行一个不允许修改甚至读取当前网页或任何其他全局状态的 Javascript 函数? Is there something like an in-browser "JS virtual machine"?是否有类似浏览器内的“JS 虚拟机”之类的东西?

How does JSFiddle ensure that you cannot run any malicious code (at the very least it could phish your login name, run a bot for the lifetime of the page, if not do much worse things)? JSFiddle 如何确保您不能运行任何恶意代码(至少它可以钓鱼您的登录名,在页面的整个生命周期内运行机器人,如果不是做更糟糕的事情)? Or doesn't it ensure that at all?或者它根本不能确保?

After much consideration and with the help of other posters in this thread (thank you so much for your help!), I found a first bunch of answers to my questions.经过深思熟虑并在此线程中其他海报的帮助下(非常感谢您的帮助!),我找到了我的问题的第一批答案。 I am re-writing my answer here though, because it summarizes the concepts and also gives you some actual code to experiment with.不过,我在这里重写了我的答案,因为它总结了概念,并为您提供了一些实际的代码供您试验。

Generally, there are two solutions to this problem: We can either use iframe or Worker to run code in an isolated environment, thus making it impossible to read or write the current page's information (which is my first major security concern).这个问题一般有两种解决方案: 我们可以使用iframeWorker在隔离的环境中运行代码,从而无法读取或写入当前页面的信息(这是我的第一个主要安全问题)。

Caja + Closure Caja + 关闭

There are more complete sandbox solutions such as Google Caja, which (by default) also runs its code in an iframe .有更完整的沙箱解决方案,例如 Google Caja,它(默认情况下)也在iframe运行其代码。 Caja does not only sandbox JS, but also HTML and CSS. Caja 不仅会沙箱 JS,还会沙箱 HTML 和 CSS。 However, it然而,它does not seem very actively maintained anymore似乎不再积极维护. .

Update: Caja has been deprecated since the original posting.更新:自最初发布以来,Caja 已被弃用。 It has been replaced with the Closure Toolkit , specifically Closure Library and Closure Templates .它已被Closure Toolkit取代,特别是Closure LibraryClosure Templates

WebWorker + Blacklisting WebWorker + 黑名单

As proposed by Juan Garcia , I am going with the web worker API, but that is not the complete story.正如Juan Garcia所提议的,我将使用 Web Worker API,但这不是完整的故事。 Even though, the worker cannot directly access anything from the hosting page, there are still quite a few security risks.尽管工作人员无法直接从托管页面访问任何内容,但仍然存在不少安全风险。 This site lists all built-ins available in a Worker's context . 该站点列出了 Worker 上下文中可用的所有内置插件

This JSFiddle demonstrates a way to run a string of code outside the window 's context without having to go through the server, which is still unsafe, as pointed out in the comments.这个 JSFiddle演示了一种在window的上下文之外运行一串代码的方法,而不必通过服务器,这仍然是不安全的,正如评论中指出的那样。

I have extended on that and employed a black-list based approach to disable all outside communication by taking away all of the following:我对此进行了扩展,并采用了基于黑名单的方法,通过删除以下所有内容来禁用所有外部通信:

  • Worker
  • WebSocket
  • XMLHttpRequest
  • importScripts

Webworker + Whitelisting网络工作者 + 白名单

The actually safest approach however, is a white-list approach, (mentioned here ), as it will stay safe into the future (given that the semantics of any whitelisted globals will never change in such a way that they allow more access in any future release; which is a somewhat reasonable assumption).然而,实际上最安全的方法是白名单方法,( 这里提到),因为它将在未来保持安全(考虑到任何列入白名单的全局变量的语义永远不会以这样的方式改变,即它们在任何未来都允许更多访问释放;这是一个有点合理的假设)。

I ended up implementing the whitelist approach in my in-browser WumpusGame that allows you to write your own AI script while playing the game, a few years back.几年前,我最终在我的浏览器 WumpusGame 中实施了白名单方法,该方法允许您在玩游戏时编写自己的 AI 脚本。 Source code here . 源代码在这里 You can see that the Whitelist is maintained in GuestScriptContext while the workers are managed by the HostScriptContext .您可以看到白名单在GuestScriptContext 中维护,而 worker 由HostScriptContext管理。

The GameScriptContext shows how you can use it. GameScriptContext展示了如何使用它。

It has several features, including guest<->host communication, guest can execute non-privileged actions (and if it has a security token even privileged actions) on the host, and a lot more.它有几个特性,包括来宾<->主机通信,来宾可以在主机上执行非特权操作(如果它有安全令牌,甚至是特权操作),等等。

NOTE: It does not play well with some extensions, as they will prevent overriding globals (when I try to run it, it complains about TEMPORARY being one of them).注意:它不适用于某些扩展,因为它们会阻止覆盖全局变量(当我尝试运行它时,它抱怨TEMPORARY是其中之一)。 The fix would be to either whitelist those, or figure out how to make it play nice with modern browser security features, or just security-focused extensions, such as AdBlock etc.解决方法是将它们列入白名单,或者弄清楚如何使其与现代浏览器安全功能配合使用,或者只是以安全为中心的扩展程序,例如 AdBlock 等。

The game is theoretically playable here , but because of reasons above it does not seem to work in Chrome (and I haven't tried other browsers yet).该游戏理论上可以在这里玩,但由于上述原因,它似乎无法在 Chrome 中运行(我还没有尝试过其他浏览器)。

Some more references:还有一些参考:

You can do this with workers.你可以对工人这样做。 The best thing about workers is that they run in a different process, so if that user code gets into an infinity loop will not hang your page.工人最好的事情是他们在不同的进程中运行,因此如果该用户代码进入无限循环,则不会挂起您的页面。 The only interface with the worker is a message interface, so strings only are exchanged which is extremely secure but limited in some situations.与 worker 的唯一接口是消息接口,因此仅交换字符串,这非常安全但在某些情况下受到限制。

As not all currently used browsers support workers, iframes are a helpful alternative.由于并非所有当前使用的浏览器都支持 worker,因此 iframe 是一个有用的替代方案。 You can create them in javascript, set display to 'none', add them to the document, get the eval function from the iframe contentWindow, then 'destroy' the iframe, like setting the outerHTML to '' (empty string).您可以在 javascript 中创建它们,将 display 设置为“none”,将它们添加到文档中,从 iframe contentWindow 获取 eval 函数,然后“销毁”iframe,例如将 outerHTML 设置为 ''(空字符串)。 It is tricky, as sometimes the iframe window get garbage collected, but if you manage to do it right, you will have an eval that has a different global object bound to it, which has an empty document.这很棘手,因为有时 iframe 窗口会被垃圾收集,但是如果您设法做到了正确,您将拥有一个绑定了不同全局对象的 eval,该对象具有一个空文档。 You can then create an interface with the code running it that global with the code running in your page global.然后,您可以创建一个接口,其中运行的代码是全局的,而代码在您的页面全局中运行。 The security depends on how do you implement that interface.安全性取决于您如何实现该接口。 You should not expose your global object, that means you should nullify the 'parent' property in the iframe global before running any user code there.你不应该公开你的全局对象,这意味着你应该在运行任何用户代码之前在 iframe 全局中取消 'parent' 属性。 You should also make sure you don't pass any Element object from the page document to the code running in the iframe global, or the user running that code will be able to navigate your whole document through the parentElement property, you should wrap them to your Element interface using Object.defineProperty for example.您还应该确保没有将页面文档中的任何 Element 对象传递给在 iframe 全局中运行的代码,否则运行该代码的用户将能够通过 parentElement 属性导航整个文档,您应该将它们包装到例如,使用 Object.defineProperty 的 Element 接口。 It is a lot of work, it did it once, but it is not sure that will be compatible between browsers.这是很多工作,它做过一次,但不确定浏览器之间是否兼容。 For example, if I well remember and I am not making a mistake, Chrome let me nullify the parent property in the iframe global, while in Safari on an iPad I wasn't able.例如,如果我记得很清楚而且我没有犯错,Chrome 让我在 iframe 全局中取消父属性,而在 iPad 上的 Safari 中我不能。 Sorry I don't have any code to share with you right now, I will post it if I have time.抱歉,我现在没有任何代码可以与您分享,如果有时间我会发布。 However, iframes scripts run in the same process than the page, which it means that an infinite loop will hang your page scripts at well, and today browser compiling the code to machine instructions can effectively hang the client os (depending of the browser and the os).但是,iframes 脚本在与页面相同的进程中运行,这意味着无限循环会很好地挂起您的页面脚本,而今天浏览器将代码编译为机器指令可以有效地挂起客户端操作系统(取决于浏览器和操作系统)。

Third you can use a sandbox, there are JavaScript self interpreters or like in TJ Crowder comment compilers that will simplify that for you, but the side effect is that it will run slower, specially interpreters.第三,您可以使用沙箱,在 TJ Crowder 注释编译器中有 JavaScript 自我解释器或类似的东西,可以为您简化它,但副作用是它会运行得更慢,特别是解释器。

IMHO, I think the folks at ECMA should better worry less about ugly arrow syntax and stuff like that and implement a secure way of running process with different global that have an interface an specific document element and its descendants but unable to get that element parent and access to the document.恕我直言,我认为 ECMA 的人最好少担心丑陋的箭头语法和类似的东西,并实施一种安全的方式来运行具有不同全局的进程,这些全局具有特定的文档元素及其后代的接口,但无法获得该元素的父元素和访问文档。 This will effectively enable a way of writing JavaScript plugins and also secure advertisement system.这将有效地启用一种编写 JavaScript 插件的方式,并且还可以保护广告系统。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用Google Caja运行用户提供的Javascript - Using Google Caja to run user-supplied Javascript 如何使用JavaScript正则表达式在单词边界之间找到用户提供的字符串? - How can I find a user-supplied string between word boundaries using JavaScript regular expressions? Web浏览器存储:允许评估用户提供的字符串对安全性有何影响? - Web browser storage: Security implications of allowing user-supplied Strings to be evaluated? 如何处理vue.js中用户提供的数据中的&#39;? - How does one handle ' in user-supplied data in vue.js? 如何以较小的音频片段发送用户提供的音频文件,以便在 js 中的服务器上处理 - How to send a user-supplied audio file in smaller audio slices to be processed on the server in js 用用户提供的控件名称作为首字母 - Make initial capital in a user-supplied control name Yeoman:使用用户提供的参数调用子生成器 - Yeoman: Call Sub-Generator With User-Supplied Arguments 读取和写入用户提供的Google表格中的行 - Read and write rows from user-supplied Google Sheet 如何在浏览器中运行javascript代码 - how to run javascript code in browser 在我的游戏中安全运行用户编写的 Javascript - Safely run user written Javascript in my game
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM