欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java中的RASP机制实现详解

程序员文章站 2023-09-04 23:28:48
rsap rasp是gartner公司提出的一个概念,称:程序不应该依赖于外部组件进行运行时保护,而应该自身拥有运行时环境保护机制; rasp就是运行时应用自我...

rsap

rasp是gartner公司提出的一个概念,称:程序不应该依赖于外部组件进行运行时保护,而应该自身拥有运行时环境保护机制;

rasp就是运行时应用自我保护(runtime application self-protection)的缩写,正如rasp字面意思一样,这是运行在运行时的一种防护技能;也就是说rasp能够在程序运行期间实施自我保护,监控与过滤有害信息,还能够拥结合程序的当前上下文实施精确、实时的防护;

java中的rasp

不严格来说java是半编译、半解释型语言,我们也都知道java中也有运行时(runtime)那java的运行时在哪呢?

不急,我们先看看java从编译到运行的流程图;

Java中的RASP机制实现详解

上图的流程为:java编译程序如javac编译.java源码文件,生成java字节码文件.class,接着.class文件进入jvm中解释执行; 从中我们可以看到java的最后执行阶段是在jvm中,也就可以说runtime运行时是jvm的重要组成部分;除此之外我们还发现

java中实现rasp的几个关键点:

1、 我们的防护程序必须能够分析、修改java的.class文件;

2、 必须在jvm解释执行.class文件时进行注入(java runtime);

通过上面的分析我们知道了要实现java的rasp所要具备的能力,然后我们发现在java中有javassist、与asm可以实现对java字节码的修改;有了修改.class字节码文件的技能,还需要能够在java运行期间注入我们的防护程序,通过上面我们发现java运行时是发生在jvm中,通过查找相关资料与jvm参数发现在jvm参数中有-javaagent参数配置java代理可以在 运行时注入我们的防护程序;

java rasp实现

在上面的分析中我们发现只要在jvm的-javaagent参数 中配置我们的保护程序,就能够轻松实现java的rasp;

java代理程序入口类需要有名为premain的静态方法 ,还需要在jar的meta-inf/mainifest.mf文件中包含 premain-class配置,下面是rasp保护程序的入口类;

javaraspapp:

/**
 * @author linx
 * @date 2017-06-25
*/
public class javaraspapp {

 public static void premain(string agentargs, instrumentation instru) throws classnotfoundexception, unmodifiableclassexception {
  system.out.println("premain");
  instru.addtransformer(new classtransformer());
 }
}

classtransformer类实现了java的代理程序机制提供的classfiletransformer接口 ,能够在运行时(runtime)对类的字节码进行替换与修改;

classtransformer也很简单,只有一个实现方法:transform,此方法中可以获取得到classloader、classname、classfilebuffer等,分别为类加载器、类名、字节码 ;

此时我们可以在transform方法中做文章,实现我们的防护程序;

/**
 * @author linxin
 * @version v1.0
 *     copyright (c) 2017 by linx
 * @date 2017/6/23.
*/
 public class classtransformer implements classfiletransformer {
 public byte[] transform(classloader loader, string classname, class<?> classbeingredefined, protectiondomain protectiondomain, byte[] classfilebuffer){
  byte[] transformeredbytecode = classfilebuffer;
  try {

    if (classname.equals("co/solinx/demo/test")) {
      system.out.println(string.format("transform start %s",classname));
      classreader reader = new classreader(classfilebuffer);
      classwriter writer = new classwriter(classwriter.compute_maxs);
      classvisitor classvisitor = (classvisitor) createvisitorins("co.solinx.demo.visitor.testvisitor", writer, classname);
      reader.accept(classvisitor, classreader.expand_frames);
      transformeredbytecode = writer.tobytearray(); 
    }
  } catch (exception e) {
    e.printstacktrace();
  }catch (throwable t){
    t.printstacktrace();
  }
  return transformeredbytecode;
}
 public object createvisitorins(final string name, classvisitor cv, string classname)
    throws nosuchmethodexception, instantiationexception, illegalaccessexception, invocationtargetexception, classnotfoundexception {
  constructor<?> ctor = class.forname(name).getdeclaredconstructor(new class[]{classvisitor.class, string.class});
  ctor.setaccessible(true);
  return ctor.newinstance(new object[]{cv, classname});
 }
}

可以看到我们在transform方法中co/solinx/demo/test类进行拦截,并通过asm修改字节码注入我们的保护逻辑,下面代码是testvisitoradapter类中的onmethodenter方法实现了通过asm调用拦截器,抛出异常的字节码;

@override
protected void onmethodenter() { 
  mv.visittypeinsn(new,"co/solinx/demo/filter/sqlfilter");
  mv.visitinsn(dup);
  mv.visitmethodinsn(invokespecial,"co/solinx/demo/filter/sqlfilter","<init>","()v",false);
  mv.visitvarinsn(astore,2);
  mv.visitvarinsn(aload,2);
  mv.visitvarinsn(aload,1);
  mv.visitmethodinsn(invokevirtual,"co/solinx/demo/filter/sqlfilter", "filter","(ljava/lang/object;)z",false);
  label label = new label();
  /**
   * ifeq filter返回值也就是栈顶int型数值等于true时跳转,抛出异常
   */
  mv.visitjumpinsn(ifeq, label); 
  mv.visittypeinsn(new, "java/sql/sqlexception");
  mv.visitinsn(dup);
  mv.visitldcinsn("invalid sql because of security check");
  mv.visitmethodinsn(invokespecial, "java/sql/sqlexception", "<init>", "(ljava/lang/string;)v", false);
  mv.visitinsn(athrow);
  mv.visitlabel(label);
  /**
   * 必须要调该方法,手动设置stack map table,否则会有 java.lang.verifyerror: expecting a stackmap frame at branch target 26异常
   * 在jdk1.7中可以使用jvm参数-usesplitverifier,关掉class验证,但jdk1.8中该参数已经去掉,所以要在1.8中运行必须调用该方法;
   */
  mv.visitframe(opcodes.f_same, 0, null, 0, null);
 } 

sqlfilter拦截类:

public class sqlfilter {
public boolean filter(object sql){
  boolean ret=false;
  system.out.println(string.format("sql filter : %s ",sql));
  if(sql.tostring().contains("1=1")){
    ret=true;
  }
  return ret;
}
}

testvisitoradapter类中的onmethodenter方法中通过调用filter拦截器,返回true就是被拦截了,抛出异常,否则放行;至此一个简单的java rasp demo就完成了; 通过后面的方式即可使用我们的rasp程序:java -javaagent:respjar-1.0-snapshot.jar app.jar;

通过rasp可以通过无嵌入、无需修改代码的实现安全保护,在rasp中可以拦截sql、会话、有害请求、ognl等等信息;

demo源码:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。