Activiti中的安全脚本如何工作

最近的Activiti 5.21.0版本的突出特点之一是“安全脚本”。 Activiti用户指南中详细介绍了启用和使用此功能的方法 。 在这篇文章中,我将向您展示我们如何实现其最终实现以及它在幕后所做的事情。 当然,由于这是我通常的签名风格,因此我们还将对性能进行一些了解。

问题

长期以来,Activiti引擎一直支持脚本任务(和任务/执行侦听器)的脚本编写。 所使用的脚本在流程定义中定义,并且可以在部署流程定义后直接执行。 这是很多人喜欢的东西。 这与Java委托类或委托表达式有很大的不同,因为它们通常需要将实际的逻辑放在类路径上。 它本身已经引入了某种“保护”,因为高级用户通常只能这样做。

但是,使用脚本不需要这种“额外步骤”。 如果您将脚本任务的功能提供给最终用户(并且我们从某些用户那里知道某些公司确实拥有此用例),那么所有的赌注都将大打折扣。 您可以通过执行流程实例来关闭JVM或执行恶意操作。

第二个问题是编写一个无限循环且永无止境的脚本非常容易。 第三个问题是,脚本在执行时可以轻松使用大量内存,并占用大量系统资源。

让我们来看入门的第一个问题。 首先,让我们添加最新和最大的Activiti引擎依赖性以及内存数据库库中的H2:

<dependencies><dependency><groupId>org.activiti</groupId><artifactId>activiti-engine</artifactId><version>5.21.0</version></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><version>1.3.176</version></dependency>
</dependencies>

我们将在这里使用的过程非常简单:只是一个开始事件,脚本任务和结束。 此处的过程并不是真正的重点,而是脚本执行。

屏幕截图-自2016-06-13-201621

我们将尝试的第一个脚本有两件事:它会获取并显示我的机器的当前网络配置(但显然有此想法的更危险的应用程序), 然后关闭整个JVM 。 当然,在适当的设置中,可以通过确保运行逻辑的用户在计算机上没有任何重要权限(但不能解决占用资源的问题)来缓解其中的某些问题。 但是我认为这很好地说明了为什么将脚本的功能提供给几乎任何人实际上在安全方面是很糟糕的。

<scriptTask id="myScriptTask" scriptFormat="javascript"><script>var s = new java.util.Scanner(java.lang.Runtime.getRuntime().exec("ifconfig").getInputStream()).useDelimiter("\\A");var output = s.hasNext() ? s.next() : "";java.lang.System.out.println("--- output = " + output);java.lang.System.exit(1);</script>
</scriptTask>

让我们部署流程定义并执行流程实例:

public class Demo1 {public static void main (String[] args) {// Build engine and deployProcessEngine processEngine = new StandaloneInMemProcessEngineConfiguration().buildProcessEngine();RepositoryService repositoryService = processEngine.getRepositoryService();repositoryService.createDeployment().addClasspathResource("process.bpmn20.xml").deploy();// Start process instanceRuntimeService runtimeService = processEngine.getRuntimeService();runtimeService.startProcessInstanceByKey("myProcess");}
}

给出以下输出(此处缩短):

—输出= eth0链接encap:以太网
inet地址:192.168.0.114广播:192.168.0.255掩码:255.255.255.0
… 流程以退出代码1完成

它输出有关我所有网络接口的信息,然后关闭整个JVM。 是的。 太恐怖了

尝试纳斯霍恩

第一个问题的解决方案是,我们需要将要在脚本中公开的内容列入白名单,并且默认情况下将所有内容都列入黑名单。 这样,用户将无法运行任何可以做恶意事情的类或方法。

在Activiti中,当javascript脚本任务是流程定义的一部分时,我们使用JDK中的ScriptEngine类将此脚本提供给JDK中嵌入的javascript引擎。 在JDK 6/7中是Rhino,在JDK 8中是Nashorn。 首先,我进行了一些认真的搜索,以找到Nashorn的解决方案(因为这将更加适用于未来)。 Nashorn确实具有“类过滤器”概念,可以有效地实施白名单。 但是,ScriptEngine抽象没有任何工具可以实际调整或配置Nashorn引擎。 我们必须做一些底层的魔术才能使其正常工作。

代替使用默认的Nashorn脚本引擎,我们自己在“ SecureScriptTask”(这是常规的JavaDelegate)中实例化Nashorn脚本引擎。 注意使用jdk.nashorn。*包的用法–不太好。 我们遵循https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/api.html中的文档,通过在Nashorn引擎中添加“ ClassFilter”来使脚本执行更加安全。 这实际上是可以在脚本中使用的已批准类的白名单。

public class SafeScriptTaskDemo2 implements JavaDelegate {private Expression script;public void execute(DelegateExecution execution) throws Exception {NashornScriptEngineFactory factory = new NashornScriptEngineFactory();ScriptEngine scriptEngine = factory.getScriptEngine(new SafeClassFilter());ScriptingEngines scriptingEngines = Context.getProcessEngineConfiguration().getScriptingEngines();Bindings bindings = scriptingEngines.getScriptBindingsFactory().createBindings(execution, false);scriptEngine.eval((String) script.getValue(execution), bindings);System.out.println("Java delegate done");}public static class SafeClassFilter implements ClassFilter {public boolean exposeToScripts(String s) {return false;}}}

当执行时,上面的脚本将不会执行,将引发一个异常,指出“线程“主”中的异常java.lang.RuntimeException:java.lang.ClassNotFoundException:java.lang.System.out.println”。

请注意,ClassFilter仅可从JDK 1.8.0_40使用(相当早!)。

但是,这不能解决无限循环的第二个问题。 让我们执行一个简单的脚本:

while (true) {print("Hello");
}

您可以猜测会做什么。 这将永远运行。 如果幸运的话,脚本任务在事务中执行时,事务超时将发生。 但这远不是一个体面的解决方案,因为它浪费了一段时间的CPU资源而无所作为。

使用大量内存的第三个问题也很容易证明:

var array = []
for(var i = 0; i < 2147483647; ++i) {array.push(i);java.lang.System.out.println(array.length);
}

启动流程实例时,内存将快速填满(仅以几个MB开头):

屏幕截图-来自-2016-06-13-204745

并最终以OutOfMemoryException: 线程“ main”中的异常java.lang.OutOfMemoryError:超出了GC开销限制

切换到犀牛

在下面的示例与上一个示例之间,花费了大量时间使Nashorn以某种方式拦截或应对无限循环/内存使用情况。 但是,经过大量搜索和试验后,似乎这些功能在Nashorn中还不是(还?)。 快速搜索将告诉您,我们不是唯一寻求解决方案的人。 通常会提到Rhino确实具有解决此问题的功能。

例如,在JDK <8中,Rhino javascript引擎具有“ instructionCount”回调机制,而Nashorn中不存在该回调机制。 它基本上为您提供了一种在回叫中执行逻辑的方法,该回叫会自动被每x条指令( 字节码指令!)调用。 我首先尝试(并且浪费了很多时间)用Nashorn模仿指令计数的想法,例如,首先美化脚本(因为人们可以将整个脚本写在一行上),然后在脚本中注入一行代码来触发回调。 但是,那是1)做起来不是很简单2)一个人仍然可以在无限运行/使用大量内存的一行上写一条指令。

搜索被困在那里,导致我们找到了Mozilla的Rhino引擎 。 自从很久以前将其包含在JDK中以来,它实际上就已经进一步发展了,而JDK中的版本并未进行这些更改! 阅读了(非常稀疏的)Rhino文档后,很明显Rhino在我们的用例方面似乎具有更丰富的功能。

Nashorn的ClassFilter与Rhino中的“ ClassShutter”概念匹配。 使用Rhino的回调机制解决了cpu和内存问题:您可以定义一个称为x指令的回调。 这意味着一行可能是数百个字节代码指令,并且每x条指令我们都会得到一个回调……。 在执行脚本时,它非常适合监视我们的cpu和内存使用情况。

如果您对我们在代码中实现这些想法感兴趣, 请在此处查看 。

这确实意味着无论您使用什么JDK版本,都不会使用嵌入式javascript引擎,而会一直使用Rhino。

尝试一下

要使用新的安全脚本功能,请添加以下依赖关系:

<dependency><groupId>org.activiti</groupId><artifactId>activiti-secure-javascript</artifactId><version>5.21.0</version>
</dependency>

这将暂时包括Rhino引擎。 这还将启用SecureJavascriptConfigurator ,在创建流程引擎之前需要对其进行配置:

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

这会将安全脚本配置为

  • 每10条指令,检查一次CPU执行时间和内存使用情况
  • 给脚本3秒3MB的执行时间
  • 将堆栈深度限制为10(以避免递归)
  • 将数组列表公开为可以在脚本中安全使用的类

从上方运行试图读取ifconfig并关闭JVM的脚本会导致:

TypeError:无法在对象[JavaPackage java.lang.Runtime]中调用属性getRuntime。 它不是功能,而是“对象”。

从上面运行无限循环脚本可以得出

线程“ main” java.lang.Error中的异常:最大variableScope时间超过了3000 ms

从上面运行内存使用脚本可以

线程“主” java.lang.Error中的异常:内存限制达到3145728字节

和欢呼! 解决了上面定义的问题

性能

我做了一个非常不科学的快速检查……我几乎不敢分享它,因为结果违背了我的设想。

我创建了一个快速主程序,该主程序运行带有脚本任务的流程实例10000次:

public class PerformanceUnsecure {public static void main (String[] args) {ProcessEngine processEngine = new StandaloneInMemProcessEngineConfiguration().buildProcessEngine();RepositoryService repositoryService = processEngine.getRepositoryService();repositoryService.createDeployment().addClasspathResource("performance.bpmn20.xml").deploy();Random random = new Random();RuntimeService runtimeService = processEngine.getRuntimeService();int nrOfRuns = 10000;long total = 0;for (int i=0; i<nrOfRuns; i++) {Map<String, Object> variables = new HashMap<String, Object>();variables.put("a", random.nextInt());variables.put("b", random.nextInt());long start = System.currentTimeMillis();runtimeService.startProcessInstanceByKey("myProcess", variables);long end = System.currentTimeMillis();total += (end - start);}System.out.println("Finished process instances : " + processEngine.getHistoryService().createHistoricProcessInstanceQuery().count());System.out.println("Total time = " + total + " ms");System.out.println("Avg time/process instance = " + ((double)total/(double)nrOfRuns) + " ms");}}

流程定义只是一个开始->脚本任务->结束。 脚本任务只是将变量添加到变量中,然后将结果保存在第三个变量中。

<scriptTask id="myScriptTask" scriptFormat="javascript"><script>var c = a + b;execution.setVariable('c', c);</script>
</scriptTask>

我运行了五次,平均每个流程实例为2.57毫秒。 这是在最近的JDK 8(所以是Nashorn)上。

然后,我切换了上面的前两行以使用新的安全脚本,从而切换到Rhino并启用了安全功能:

SecureJavascriptConfigurator configurator = new SecureJavascriptConfigurator().addWhiteListedClass("org.activiti.engine.impl.persistence.entity.ExecutionEntity").setMaxStackDepth(10).setMaxScriptExecutionTime(3000L).setMaxMemoryUsed(3145728L).setNrOfInstructionsBeforeStateCheckCallback(1);ProcessEngine processEngine = new StandaloneInMemProcessEngineConfiguration().addConfigurator(configurator).buildProcessEngine();

再次进行了五次运行……并获得了1.07毫秒/流程实例。 这是同一件事的两倍以上

当然,这不是一个真正的考验。 我以类白名单检查和回调为前提,假设Rhino的执行速度会变慢,但是没有这种事情。 也许这种特殊情况更适合Rhino……如果有人可以解释,请发表评论。 但这仍然是一个有趣的结果。

结论

如果您在流程定义中使用脚本,请仔细阅读引擎中的此新安全脚本功能。 由于这是一项新功能,非常欢迎反馈和改进!

翻译自: https://www.javacodegeeks.com/2016/06/secure-scripting-activiti-works.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/353709.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用准现网的数据,使用本地的样式脚本,本地调试准现网页面(PC适用)

原理&#xff1a; 本地逻辑&#xff0c;重新渲染 步骤&#xff1a; 1.安装插件&#xff1a;Tampermonkey 度盘&#xff1a;https://pan.baidu.com/s/1bpBVVT9 2.设置&#xff1a; 点击插件-->仪表盘 添加脚本 将此文本文档中的脚本复制到脚本编辑框处&#xff0c;并CtrlS保存…

FDATOOL设计滤波器

FDATOOL设计滤波器 分类&#xff1a; 数字信号处理 2006-04-20 11:251. 在Matlab中键入fdatool运行Filter Design and Analysis Tool。具体使用请参见Matlab Help中的Signal Processing Toolbox->FDATool。 2. 在fdatool工具中应该注意的几个问题&#xff1a;(a)Fstop&#…

大例外背后的真相

异常可能是最被滥用的Java语言功能。 这就是为什么 让我们打破一些神话。 没有牙仙子。 圣诞老人不是真实的。 TODO评论。 finalfinalversion-final.pdf。 无皂肥皂。 而且…例外实际上是例外。 后者可能需要更多说服力&#xff0c;但我们可以帮助您。 在这篇文章中&#xff…

MATLAB里面的filter和filtfilt的C语言源代码

MATLAB里面的filter和filtfilt的C语言源代码 嗯&#xff0c;算法非常简单&#xff0c;就是网上搜不到C代码实现。filter是个很万能的数字滤波器函数&#xff0c;只要有滤波器的差分方程系数&#xff0c;IIR呀FIR呀都能通过它实现。在MATLAB里面&#xff0c;filter最常用的格式是…

20172302『Java程序设计』课程 结对编程练习_四则运算第二周阶段总结

一.结对对象 姓名&#xff1a;周亚杰学号&#xff1a;20172302担任角色&#xff1a;驾驶员&#xff08;周亚杰&#xff09;伙伴第二周博客地址二.本周内容 (一)继续编写上周未完成代码 1.本周继续编写代码&#xff0c;使代码支持分数类计算 2.相关过程截图 a.下图是上周编写的生…

实践中的弹性基础架构

几周前&#xff0c;我获得了一个难得的机会&#xff0c;可以在基础设施领域弄脏双手。 在JVM内部的深入了解下&#xff0c;我每天的工作经历发生了有趣的变化&#xff0c;我想与您分享动机和成果。 希望它可以启发类似的问题类别。 背景 我将从解释需要解决方案的上下文开始。…

notepad++插件实现json、xml格式化

notepad比较出色的免费的数据编辑、格式化工具。。。 现在json、xml文件很流行、格式化也是必须的&#xff0c;方便查看关键信息&#xff01; 01、下载notepad及相关插件 npp_7.5.5-x86&#xff1a; https://files.cnblogs.com/files/xiaochina/npp_7.5.5-x86.zip npp-json:…

ActiveMQ 5.x中的消息持久性

我被问了很多关于ActiveMQ如何存储消息&#xff08;或在某些情况下不存储&#xff09;的基本知识。 这是它的高级解释。 注意&#xff0c;上下文在JMS中。 如果您使用ActiveMQ的非JMS客户端&#xff08;即STOMP&#xff0c;AMQP&#xff0c;MQTT等&#xff09;&#xff0c;则在…

一个select元素自定义设计的新思路:appearance: none之后利用符号制造小箭头

最近工作时解决了一个前端小问题&#xff08;如下图所示&#xff09;&#xff1a;在Safari中&#xff0c;select的控件之上有不和谐的灰色部分。 刚开始时我以为是backgrand或是border设置不当之类产生的问题&#xff0c;在搜索了很久之后终于找到了问题所在&#xff1a;这个灰…

调整HashMap的大小:未来的危险

最近&#xff0c;我偶然发现了一个错误&#xff0c;该错误是由于多个线程对java.util.HashMap的使用不当引起的。 该错误是抽象泄漏的一个很好的例子。 只有了解数据结构的实现级别详细信息&#xff0c;才能帮助我解决当前的问题。 因此&#xff0c;我希望分享我所面临的问题将…

别的程序员是怎么读你的简历的

别的程序员是怎么读你的简历的 2009年11月9日 陈皓 下面这个图片来源国外&#xff0c;是一个关于程序员面试时的简历&#xff0c;被人事部门和程序员本身评审的角度不同的图片。当然&#xff0c;这是一个从国外面试的视角制作的图片&#xff0c;不过&#xff0c;可以看出&#…

Zabbix linux agent 安装

系统&#xff1a;Linux Centos 7.3 x64 服务&#xff1a;Zabbix_agent 3.0.16 一.安装Zabbix_agent 服务 1.安装zabbix 3.0 yum源 rpm -ivh http://repo.zabbix.com/zabbix/3.0/rhel/7/x86_64/zabbix-release-3.0-1.el7.noarch.rpm 2.安装Zabbix_agent yum install zabbix-agen…

直接在apk中添加资源的研究

原文 http://blog.votzone.com/2018/05/12/apk-merge.html 之前接手过一个sdk的开发工作&#xff0c;在开发过程中有一个很重要的点就是尽量使用代码来创建控件&#xff0c;资源文件最好放到assets目录下&#xff0c;如果必须使用res资源&#xff0c;需要通过 getResources().g…

JavaFX实际应用程序:SkedPal

“真实世界的应用程序”系列中的一个新条目。 这次是SkedPal &#xff0c;这是一个用于智能管理忙人生活的应用程序。 我一直在咨询SkedPal团队有关JavaFX的事宜&#xff0c;并且在他们决定开始使用我的CalendarFX框架来满足他们的日历要求时&#xff0c;我也在咨询他们。 在下…

chromium之histogram.h

histogram不知道是干啥的 // Histogram is an object that aggregates statistics, and can summarize them in // various forms, including ASCII graphical, HTML, and numerically (as a // vector of numbers corresponding to each of the aggregating buckets). google翻…

viewobject_只读ViewObject和声明性SQL模式

viewobject介绍 声明式SQL模式被认为是基于实体的视图对象的最有价值的优点之一。 在这种模式下&#xff0c;根据UI中显示的属性在运行时生成VOSQL。 例如&#xff0c;如果某个页面包含一个只有两列EmployeeId和FirstName的表&#xff0c;则查询将生成为“从Employees中选择Emp…

MyEclipse6.0 安装axis2插件, 调用加密的SAP webservice

MyEclipse6.0 安装axis2插件, 调用加密的SAP webservice 6人收藏此文章, 我要收藏 发表于1个月前(2013-06-06 09:41) , 已有116次阅读 &#xff0c;共0个评论 首先鄙视一下自己&#xff0c;还在用myeclipse,竟然还是6.0版本&#xff0c;没办法&#xff0c;用习惯了&#xff0c…

Eclipse中要导出jar包中引用了第三方jar包怎么办

Eclipse中要导出jar包中引用了第三方jar包怎么办 (2009-07-20 15:28:44) 转载▼标签&#xff1a; it 分类&#xff1a; Eclipse 今天做个小的java程序&#xff0c;想要先将其导出成一个可执行的jar包&#xff01;向往常一样&#xff0c;单击菜单栏中的 File -> export,弹出…

拖动滑块拼图背景图没显示_计划B? 那是计划N…没什么。 拼图于2015年问世

拖动滑块拼图背景图没显示真是一天 当典型的欧洲人逐渐破产时&#xff0c;美国的人们开始喝咖啡。 这就是为什么我在Mark Reinhold最近的新闻中睡个好觉的原因。 他在题为“ Project Jigsaw&#xff1a;火车晚点 ”的帖子中建议将Project Jigsaw推迟到下一个版本Java 9。 在最近…

java keytool证书工具使用小结

Keytool 是一个Java数据证书的管理工具 ,Keytool将密钥&#xff08;key&#xff09;和证书&#xff08;certificates&#xff09;存在一个称为keystore的文件中在keystore里&#xff0c;包含两种数据:密钥实体&#xff08;Key entity&#xff09;-密钥&#xff08;secret key&a…