在沙箱中运行客户端代码的合适语言

15

我希望在服务器上模拟(不安全的)客户端代码,并寻找一种适合的语言来实现。最好能够让客户端使用与我用于模拟的相同语言编写。

  • 安全性是首要考虑因素
  • 最好是众所周知的语言(易于客户端学习语法)
  • 应该很容易禁用/启用沙盒中可用的语言功能
  • 如果我能够逐步模拟代码,那就更好了

理想情况下,我只需构建几个接口(并发布这些接口),加载客户端代码,并通过允许其仅使用我的接口+我精心选择的标准API子集来模拟该代码。

在这个模拟过程中,我应该能够限制客户端代码使用的资源(时间和内存)。如果我能够逐步模拟代码,那就更好了,这样我就可以始终返回确定性解决方案。

性能真的不是问题。想法是允许客户为小游戏/难题编写自定义AI。游戏将在服务器上模拟,并将结果返回给用户。

最初,我考虑自己构建外部DSL,包括解析器和评估器,但也许有现成的解决方案?

5个回答

5
我的选择是使用一些脚本语言,可以在不自动提供某些广泛框架(如.Net或Java)的情况下使用 - 添加功能比限制它们更容易。游戏引擎脚本语言,例如LUA可能是一种选择,并且通常带有多个平台的实现可供使用。
一般考虑:
无论您选择哪种语言/框架,请确保您能够从以下风险中恢复/接受:
• 致命异常(例如由于递归函数导致的堆栈溢出) • 无限制的内存分配/内存不足异常 • 长时间运行的任务
注意不要公开允许用户在您的控制之外创建新线程/任务/同步对象(锁定/信号量)的API,或依赖提供此类API的平台。允许这样的方法可能会将您服务器的资源开放给无限消耗或DOS /死锁……
请注意,长时间运行的任务对于任何合理的语言都是一个问题,因为您无法通过查看它来确定程序是否会结束- halting problem。无论您选择什么平台,您都必须找出解决方案。
.Net/C#:
你可以查看 Terrarium,它在 .Net 中实现了这一点——在沙盒环境中运行用户机器上的不受信任的代码。
.Net 提供了一种限制多个 API 使用的方法——如何在沙盒中运行部分受信任的代码 是一个很好的起点。请注意,正如 @Andrew 指出的那样,除了基本的沙盒保护之外,验证用户提供的程序集(直接提供或编译自用户源代码)是否使用你不喜欢的 API(甚至反过来——只使用你允许的 API)是一个好主意。在单独的 AppDomain 中运行的部分受信任的代码可以为你提供对不太敌对的代码的良好保护。
通常很难防止 堆栈溢出,需要在 .Net 中使用自定义主机进行处理。长时间运行的任务可以通过 Thread.Abort 终止或关闭带有用户代码的 AppDomain。

1

Java有SecurityManager的概念,它使您能够微调虚拟机中可以运行什么或不能运行什么。

它还允许您编译代码并在运行时加载生成的类。然后,您可以运行这些类中的任何代码,前提是SecurityManager不会因为操作不被允许而抛出SecurityException。

这篇文章展示了一个编写的例子,在运行时编译、加载和运行一些代码(以文本源代码提供)。

这篇其他文章提供了运行不受信任(可能是恶意的)代码的说明。


+1. 请问您能否检查/评论我在答案中提到的问题(如SO、OOM、长时间运行的代码...)是否适用于Java,并提供应对方法吗? - Alexei Levenkov
SO和OOM可以在一个捕获所有块中捕获,该块退出运行外部代码的线程并释放资源。如果长时间运行的代码在同一JVM中的另一个线程中运行,则可能会更加棘手(Java实际上不提供“终止”线程的机制) - 但是,如果存在风险,则可以在不同的进程(即另一个JVM)中运行代码,在这种情况下,简单调用操作系统即可终止该进程。 - assylias

1
如果您想运行由最终用户提供的代码,并希望使用他们可能已经了解的语言,为什么不使用JavaScript呢?
在WebWorker中沙箱化JavaScript是可能的(一个与主JavaScript应用程序隔离并且没有访问共享内存或全局变量(如Window对象和DOM)的并发线程,并且只有与主线程进行通信的单个途径)。
我能想到的唯一安全问题是限制一个人消耗的硬件资源,但我还没有研究过 - 使用JavaScript运行时之一可能很好地实现这一点。您还需要找到一种方法来防止一个WebWorker生成其他WebWorker。您必须添加一些额外的代码,以确保某人的WebWorker在一定时间后自动关闭。
我还没有尝试过服务器端WebWorkers,但从NodeJS、Rhino和PhantomJS的外观来看,它们都支持它。Node和Rhino提供了与典型Web浏览器不同的环境,而PhantomJS是一个运行无头的完整浏览器引擎(WebKit)。从WebWorker的角度来看,它们可能都看起来相同。

此外,如果您希望在WebWorkers本身的基础上进行额外的沙箱处理,Rhino可以将整个JavaScript引擎运行在一个受到沙箱保护的JVM中。 - Richard Connamacher
确实。像SecurityManager这样的东西可以用来在脚本代码周围增加额外的隔离层。此外,最大的好处是除了脚本本身之外,大多数代码都可以用强类型语言编写,这对于像具有自定义脚本AI的游戏之类的大项目来说是真正的优势。+1 - Stijn de Witt

0
我建议使用.NET(C#,VB和F#)。您可以利用JIT进行服务器端程序化编译代码,使用反射进行分析,并使每个客户端在单独的AppDomain中运行以实现安全性和代码隔离。

我希望避免自己使用反射来处理提供的代码。我希望找到一种语言/环境,可以为我编译代码,并检查它是否符合我的约束条件,然后拒绝或接受这段代码。被接受的代码应该是安全的,可以让我随意运行(多次)。 - Antiz
.NET 允许您以编程方式编译代码。我相信您还可以向抽象类添加安全属性,这样也应该能在某种程度上进行控制。 - poy
AppDomains 看起来很有趣,希望有其他有经验的人能够过来支持你的观点。 - Antiz
+1. 我在我的帖子中添加了更多的 .Net 链接。请注意,“反射”似乎被用于更广泛的意义 - 您需要 IL 读取器来实际分析编译代码。 - Alexei Levenkov

0

如果你真的想要“众所周知”,ADsafe是JavaScript的一个子集,它被有效地隔离了起来,尽管它有一些怪癖(例如避免this)。

Java有“类加载器”,可以限制类对其他类的访问(参见SecureClassLoader)。我对细节有点模糊,但它基本上是用于提供Java小程序安全性的。我不知道它是否可以限制内存使用,但限制CPU时间并不太困难(不要让它生成线程,并在超时后杀死运行不受信任代码的线程)。

(我想起了Robocode,它运行未经信任的AI试图在游戏的限制下杀死其他未经信任的AI。主要区别在于它旨在在最终用户的计算机上运行,尽管有网站进行自动排名。这是我接触Java的第一步,尽管我注意到它现在支持.NET,可能是由于两种语言的相似性。)


Robocode看起来确实很像我正在尝试做的事情,但已经更加先进了。我怀疑我是否可以通过Java禁用诸如“while”、“foreach”等结构,对吗?ADsafe很有趣,但我认为它更适合处理DOM,而不是与我定义的自定义接口进行交互。但我可能错了。 - Antiz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接