返回介绍

18.12. 安全脚本

发布于 2023-09-17 23:40:34 字数 3487 浏览 0 评论 0 收藏 0

默认情况下,使用脚本任务时,执行的脚本与Java代码具有相似的能力。可以完全访问JVM,永远运行(无限循环),或占用大量内存。

相较而言,Java代码需要放在classpath的jar中,与流程定义的生命周期不一样。最终用户一般不会撰写Java代码,这基本上是开发者的工作。

而脚本是流程定义的一部分,生命周期一致。脚本任务不需要额外的jar部署步骤,而是在流程部署后就可以执行。有时,脚本任务中的脚本不是由开发者撰写的。所以会产生这个问题:脚本可以完全访问JVM,也可以在执行脚本时阻塞许多系统资源。因此允许执行来自任何人的脚本并不是一个好主意。

可以启用安全脚本功能解决这个问题。目前,这个功能只实现了javascript脚本,在项目中添加flowable-secure-javascript依赖启用。Maven:

<dependency>
  <groupId>org.flowable</groupId>
  <artifactId>flowable-secure-javascript</artifactId>
  <version>${flowable.version}</version>
</dependency>

添加这个依赖会同时引入Rhino依赖(参见https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino)。Rhino是一个用于JDK的javascript引擎。过去包含在JDK6与7中,并已被Nashorn引擎取代。然而,Rhino项目仍然在继续开发。许多(包括Flowable用于实现安全脚本的)功能都在之后才加入。在撰写本手册的时候,Nashorn还没有实现安全脚本功能需要的功能。

这意味着脚本可能存在一些(基本很少)区别(例如,Rhino使用importPackage,而Nashorn使用load())。

通过专门的Configurator对象配置安全脚本,并在流程引擎实例化之前将其传递给流程引擎配置:

SecureJavascriptConfigurator configurator = new SecureJavascriptConfigurator()
  .setWhiteListedClasses(new HashSet<String>(Arrays.asList("java.util.ArrayList")))
  .setMaxStackDepth(10)
  .setMaxScriptExecutionTime(3000L)
  .setMaxMemoryUsed(3145728L)
  .setNrOfInstructionsBeforeStateCheckCallback(10);

processEngineConfig.addConfigurator(configurator);

可以使用下列设置:

  • enableClassWhiteListing: 为true时,会将所有类加入黑名单。需要在白名单中添加希望运行的所有类,严格控制暴露给脚本的东西。默认为false

  • whiteListedClasses: 一个全限定类名字符串的集合,表示允许在脚本中使用的类。例如,要在脚本中使用execution对象,需要在这个集合中添加org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl字符串。默认为

  • maxStackDepth: 限制在脚本中调用函数时的最大栈深度。可以用于避免在脚本中递归调用方法而导致的栈溢出异常。默认为-1(禁用)。

  • maxScriptExecutionTime: 脚本允许运行的最大时间。默认为-1(禁用)。

  • maxMemoryUsed: 脚本允许使用的最大内存数量,以字节计。请注意脚本引擎自己也要需要一定量的内存,也会算在这里。默认为-1(禁用)。

  • nrOfInstructionsBeforeStateCheckCallback: 脚本每执行x个指令,就通过回调函数进行一次脚本执行时间与内存检测。请注意这不是指脚本指令,而是指java字节码指令(一行脚本可能有上百行字节码指令)。默认为100。

请注意:maxMemoryUsed设置只能用于支持com.sun.management.ThreadMXBean#getThreadAllocatedBytes()方法的JVM,如Oracle JDK。

ScriptExecutionListener与ScriptTaskListener也有安全形式:org.flowable.scripting.secure.listener.SecureJavascriptExecutionListenerorg.flowable.scripting.secure.listener.SecureJavascriptTaskListener

像这样使用:

<flowable:executionListener event="start" class="org.flowable.scripting.secure.listener.SecureJavascriptExecutionListener">
  <flowable:field name="script">
  <flowable:string>
    <![CDATA[
      execution.setVariable('test');
    ]]>
  </flowable:string>
  </flowable:field>
  <flowable:field name="language" stringValue="javascript" />
</flowable:executionListener>

可以通过GitHub上的单元测试, 查看不安全脚本以及通过安全脚本功能将其变得安全的例子,

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文