delight-nashorn-sandbox JsSanitizer 分析

该博客介绍了如何在Java环境中使用Nashorn沙箱安全执行JavaScript代码,通过代码格式化和注入中断检测函数来确保执行过程的可控性。当JS执行超时或内存限制达到时,能够优雅地中断执行。主要涉及的步骤包括代码美化、循环内函数注入以及异常处理机制。此外,还展示了ThreadMonitor如何监控JS线程的CPU和内存使用,以及在异常情况下如何中断执行。
摘要由CSDN通过智能技术生成

sanitizer: 防腐剂,消毒杀菌剂,洗手液 =》应该是指对js代码进行消杀


检查循环语句,并插入函数调用,用来当js执行引擎线程被interrupt中断时,break当前脚本的执行

版本:delight-nashorn-sandbox-0.2.0  (Java嵌入式js解析引擎的沙箱环境封装)

1. 流程

public方法只有secureJs,主要逻辑在secureJsImpl中

  • 格式化js代码    =》 对应beautifyJs  (TODO 格式化后,nashorn执行更快)
  • 如果有循环js代码,则在循环中注入函数调用,(用来检测中断,抛出异常,进而中断当前js执行线程)             =》 对应injectInterruptionCalls

2. 格式化js代码

在pom.xml文件中

		<dependency>
			<groupId>org.webjars.bower</groupId>
			<artifactId>js-beautify</artifactId>
			<version>1.9.0</version>
		</dependency>

 依赖 org.webjars.bower:js-beautify:1.9.0
使用其中的格式化js库: /META-INF/resources/webjars/js-beautify/1.9.0/js/lib/beautifier.js
 

3. 注入函数调用

内部类PoisonPil: Poison即毒药,毒物,Pil,即Pills药丸,=》应该是指有危害的js代码片段,用正则来检测有危害的js代码,并替换对应的js代码replacement

比如:

new PoisonPil(Pattern.compile("(\\s*for\\s*\\([^\\{]+\\)\\s*\\{)"), JS_INTERRUPTED_FUNCTION + "();") // for with block
匹配     for ( ) {
替换     __if();        =》 实际上是把匹配上的内容,在后面添加该[替换内容],使用了matcher.appendReplacement(sb, ("$1" + pp.replacement))  $1表示匹配内容
上述替换,可以理解为js中的函数调用

  

如果有注入js代码,则在js代码开头再添加其他代码,getPreamble(preamble,序言,绪论,开场白):
var __it=Java.type('delight.nashornsandbox.internal.InterruptTest');
var __if=function(){__it.test();};   
这里,首先把在js中引入Java类类型InterruptTest
然后,定义js函数__if,函数体即为调用InterruptTest的静态方法test,检测线程中断状态,并抛出异常

  

4. 例子

输入:           
var x = 1;while (true) {x=x+1;}

格式化beautify: 
var x = 1;
while (true) {
 x = x + 1;
}

注入(loop):
var x = 1;
while (true) {__if();
 x = x + 1;
}

注入(添加开头):
var __it=Java.type('delight.nashornsandbox.internal.InterruptTest');var __if=function(){__it.test();};
var x = 1;
while (true) {__if();
 x = x + 1;
}

5. 作用

  • 执行js代码时,是在线程池的线程中运行
  • 同时,调用方,会执行ThreadMonitor中的run方法,监控js线程,比如CPU、内存,
  • 如果超时,或者超出内存限制,则(在Java代码中)会向js线程发送interrupt中断信号(threadToMonitor.interrupt())    相比thread.stop()终止线程,更优雅一些;TODO 并且,如果不在js中注入中断检测函数调用,在Java中使用thread.interrupt()时,js执行线程没有自动捕获或者检测线程中断状态,进而退出js执行
  • 此时,js线程中注入的代码,会检测线程中断状态(在js代码中注入,执行的却是Java代码),并抛出异常
  •  这样,js线程的调用方,JsEvaluator,会捕获到该异常,设置状态,并退出
  •  (实际上,应该不是js线程吧,JsEvaluator是Java线程,调用执行编译后的js代码)

ThreadMonitor

	void run() {
		try {
			// wait, for threadToMonitor to be set in JS evaluator thread
			synchronized (monitor) {
				if (threadToMonitor == null) {
					monitor.wait((maxCPUTime + 500) / MILI_TO_NANO);
				}
				if (threadToMonitor == null) {
					timedOutWaitingForThreadToMonitor = true;
					throw new IllegalStateException("Executor thread not set after " + maxCPUTime / MILI_TO_NANO + " ms");
				}
			}
			final long startCPUTime = getCPUTime();
			final long startMemory = getCurrentMemory();
			while (!stop.get()) {
				final long runtime = getCPUTime() - startCPUTime;
				final long memory = getCurrentMemory() - startMemory;
				
				if (isCpuTimeExided(runtime) || isMemoryExided(memory)) {
					
					cpuLimitExceeded.set(isCpuTimeExided(runtime));
					memoryLimitExceeded.set(isMemoryExided(memory));
					threadToMonitor.interrupt();
					synchronized (monitor) {
						monitor.wait(50);
					}
					if (stop.get()) {
						return;
					}
					if (!scriptFinished.get()) {
						LOG.error(this.getClass().getSimpleName() + ": Thread hard shutdown!");
						threadToMonitor.stop();
						scriptKilled.set(true);
					}
					return;
				} else {
					
				}
				synchronized (monitor) {
					long waitTime = getCheckInterval(runtime);
					
					if (waitTime == 0) {
						waitTime = 1;
					}
					monitor.wait(waitTime);
				}
				
			}
		} catch (final Exception e) {
			throw new RuntimeException(e);
		}
	}

JsEvaluator implements Runnable

  @Override
  public void run() {
    try {
      boolean registered = threadMonitor.registerThreadToMonitor(Thread.currentThread());
      if (registered) {
        result = operation.executeScriptEngineOperation(scriptEngine);
      }
    }
    catch (final RuntimeException e) {
      // InterruptedException means script was successfully interrupted,
      // so no exception should be propagated
      if(!(e.getCause() instanceof InterruptedException)) {
        exception = e;
      }
    }
    catch (final Exception e) {
      exception = e;
    } 
    finally {
      threadMonitor.scriptFinished();
      threadMonitor.stopMonitor();
    }
  }

参考:

https://github.com/javadelight/delight-nashorn-sandbox

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值