【问题标题】:security considerations when using (end-user-defined) JavaScript code inside Java在 Java 中使用(最终用户定义的)JavaScript 代码时的安全注意事项
【发布时间】:2021-08-12 12:55:05
【问题描述】:

我正在开发一个 Java 项目。在其中,我们希望最终用户能够定义基于一组给定的原始类型或字符串变量计算的变量。在某些时候,所有给定的变量都设置为特定的值,然后应该进行计算。然后必须将所有生成的计算变量发送到 Java。

我正在评估最终用户定义计算的方法。 (当前)想法是让他编写 JavaScript 并让该代码在 Java 程序中解释/执行。我知道有两种方法可以做到这一点:使用 javax.scripting API 或 GraalVM/Truffle。在这两种情况下,我们都会这样做:

  1. 给定的变量被赋予到脚本中。在 javax.scripting 中通过ScriptEngine.put,在 Graal/Truffle 通过Value.putMember
  2. 最终用户可以在全局上下文中定义变量(其名称不得与来自 Java 的名称冲突)。他如何设置它们的值取决于他 - 他可以直接设置它们(到一个常数,到给定变量之一,到其中一些的总和......)或定义对象和函数并通过调用这些来设置值.
  3. 当给定变量具有固定值时,将执行脚本。
  4. 脚本在全局上下文中定义的所有变量都将发送到 Java。在 javax.scripting 中通过ScriptEngine.get,在 Graal/Truffle 通过Value.getMember

注意:我们不会授予脚本访问任何 Java 类或方法的权限。在 javax.scripting 中通过检查脚本是否包含字符串 Java.type(并禁止这样的脚本),在 Graal/Truffle 中通过使用默认的 Context(具有 allowAllAccess=false)。

互联网上有很多关于 JavaScript 安全问题以及如何避免这些问题的提示和技巧。一方面,我觉得它们都不适用于这里(解释如下)。另一方面,我不太了解 JavaScript - 除了纯粹的、无副作用的计算,我从未将它用于其他任何事情。

所以我在这里寻找一些指导:在这种情况下可能会出现什么样的安全问题?


为什么我在这种情况下看不到任何安全问题:

这是纯 JavaScript。它甚至不允许创建可用于例如在磁盘上创建一个文件。我知道 JavaScript 不包含任何逃避其沙箱的功能(如文件访问、线程、流......),它仅能够操纵提供给其沙箱的数据。见https://262.ecma-international.org/11.0/#sec-overview这部分:

ECMAScript 是一种面向对象的编程语言,用于执行 计算和操作主机内的计算对象 环境。此处定义的 ECMAScript 并非旨在 计算自给自足;确实,没有规定 本规范用于外部数据的输入或计算的输出 结果。 相反,预计 ECMAScript 程序不仅会提供对象和其他 本规范中描述的设施,但也某些 环境特定的对象,其描述和行为是 超出本规范的范围,除非表明它们 可能会提供某些可以访问的属性和某些 可以从 ECMAScript 程序调用的函数。

我们场景中的沙盒只放入一些无害的玩具(即给定的原始类型或字符串变量),在孩子玩过它们(脚本运行)后,生成的建筑物(用户定义的变量) 被取出来保存它们(在 Java 程序中使用)。

【问题讨论】:

    标签: javascript java security javax.script graaljs


    【解决方案1】:

    (1) 在虚拟机中运行的代码可能会逃逸。即使对于众所周知的 JS 实现,例如 V8,这个 commonly happens。通过在您的服务器上运行不受信任的代码,只要已知此类漏洞,您就很容易受到攻击。您绝对应该为此做好准备,进行风险评估,例如在引擎运行的(虚拟)机器上可以访问哪些其他数据(其他客户数据?,秘密?),并针对这些数据加强您的基础设施。

    (2) 它会停止吗?如果客户运行 while(true); 会发生什么?这会使您的服务器崩溃吗?可以通过在某个超时后终止执行来防御这种情况(不要尝试验证代码,这永远不会可靠地工作)。

    (3) 使用的资源是否有限(内存)?使用a = ""; while(true) a += "memory"; 可以轻松分配大量内存,对其他程序产生负面影响。还应确保内存使用受到限制,以便在资源耗尽之前终止程序。

    【讨论】:

    • 虽然第二个在理论上是无法解决的问题,但可以通过只给代码有限的时间来完成或将其视为已损坏(即以超时启动它)来轻松避免。跨度>
    【解决方案2】:

    只是一些想法。您本质上是在询问您是否可以信任您的沙盒/虚拟机,因为您应该假设您使用的是一个好的,或者唯一确定的方法是自己阅读它的所有源代码。如果您选择一个受信任且广为人知的沙盒,我猜您可以信任它(javascript 不应该能够影响它之外的文件系统内容)。

    另一方面,您为什么不只是在客户端进行所有这些计算,然后将结果发送到您的后端,似乎需要进行很多设置才能在服务器端运行 JavaScript。如果对此的论点是“不作弊”或类似的东西,那么即使您的代码已发送到服务器(您不知道是谁向您发送了该 javascript),您也无法避免这种情况。在我看来,做这个设置只是为了在服务器端运行它是没有意义的,只是在客户端运行它。

    如果您确实需要在服务器端使用它,那么您需要考虑您的 java 是否以 root 权限运行(在这种情况下,它可能还会以 root 权限调用沙箱)。在我的设置中,我的 nodejs 在 ~/home 下执行,所以即使发生最坏的情况并且有人设法删除了所有他们能做的最坏的事情,就是清除主目录。如果您正在运行 javascript 服务器端,那么我强烈建议您至少不要在 root 下这样做。它不应该在那个沙箱之外做任何事情,但至少即使在最坏的情况下它也不能消灭你的服务器。

    我会考虑的其他事情(因为我不知道您的沙箱允许或限制什么)是您是否可以在该沙箱(或类似的东西)中请求和使用 javascript 进行 API 调用,因为如果它在 root 下运行并允许它将授予某人对您的基础架构的 root 访问权限(您的基础架构认为它是您的服务器发出请求,而实际上它是恶意 JS 代码)。

    您也可能犯了错误,或者使用不正确的参数或缺少配置选项启动虚拟机,并且它突然允许在您不知道的情况下出现漏洞,因此您必须确保正确设置它.

    另外一点是,如果您曾经将该 JS 存储在某个数据库中,而不仅仅是执行它,那么您必须确保它不会直接提供给任何其他用户而不检查它,否则您会发生 XSS。例如,您为“编码测试”构建了一个应用程序并将其测试结果存储在数据库中,然后您想将该结果显示给潜在雇主,如果您直接向他们显示该结果,您将执行恶意代码他们的浏览器。

    但我真的不明白为什么你应该关心这些,只需在客户端运行它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-24
      • 2011-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多