所以我主要尝试简单的架构,其中我制作一个通用接口的存储库,然后将其拉到每个插件上,然后实现它,然后调用主应用程序添加插件并在运行时动态运行它。
interface PluginI {
val core: CoreApplication // Means of communication with core app, this obj is sent by core app by constructor
fun version(): Double
suspend fun load(pluginConfiguration: PluginConfiguration)
suspend fun run()
}
但是,如何将插件限制在某些区域,例如防止可能破坏,劫持或崩溃主应用程序?特别是应该限制它使用jvm的静态类中的任何内容,例如System,一些劫持应用程序的一个例子是它可以执行一个可以在shell中执行漏洞的
System.getRuntime().exec()
。
沙盒不是解决方案,还是解决方案?因为它只是断开了主应用程序和插件之间的连接。
我正在寻找一种解决方案,该解决方案仅提供一个共享对象,主应用程序从该对象与插件通信并发送一些它想要的信息,例如日期/时间或任何不会损害运行时的信息。
在 Java中创建沙盒环境几乎是不可能的,使用 Java 9+,您可以使用模块来简化此操作......特别是如果你想允许某种反射。
但是允许其他反射确实很难且有风险,您所做的一切都绝对应该作为白名单,黑名单只是"不起作用"(也就是,我不相信任何人都能够找到要包含的所有内容并记住在更新任何依赖项时保持更新(。
Java有一个内置系统,它被称为SecurityManager,你的应用程序应该设置自己的SecurityManager并使用它来过滤每个方法调用,只允许从调用它的同一插件调用方法并阻止任何类型的反射,阻止更改安全管理器并阻止任何手动类加载。
此外,Java 9 模块可用于简化此设置,因为模块甚至可以阻止模块之间的调用,同时不限制对同一模块的类的反射。但它不能完全取代安全管理器。
还要记住,无论你做什么,仍然有人会导致你的应用程序变得不稳定,特别是如果你允许任何类型的 I/O 或自己的线程。但是插件仍然可以分配大量内存或只是运行无限循环 - 或者实际上只是同时运行两者;) 在 java 中,没有办法强制线程停止或限制某些代码分配的内存量。
我能看到的唯一部分解决方案是使用 java 代理来检测每个插件的代码并添加调用,检查线程是否在任何可能运行太长时间的代码中被中断。
分配也是如此。
但是,您还需要 100% 确定没有一个白名单方法可以一次循环或分配太多。
基本上:不要。
如果您可以稍微信任插件,但您只想设置一些规则以避免不良做法,那么这只是一个好的解决方案(没有代理(。它不会阻止想要破坏某些东西的人,但会阻止典型的程序员创建不干净的代码。
如果需要运行不受信任的代码...要么在操作系统级别的实际沙盒中运行它,比如查看 Sphere 引擎,并且只以某种安全的方式与之通信。
或者使用其他一些语言,让你做与上面所说的相同,但更容易,比如Lua。 http://lua-users.org/wiki/SandBoxes 然后,您可以使用脚本引擎从 Java 运行此类语言 https://github.com/luaj/luaj 但即便如此,也很难 100% 确定它会正常工作,并且没有人会发现和使用漏洞,因为如果攻击者所需要的只是对 cpu/内存施加足够的压力以破坏主应用程序,则不需要太多。