RSAP

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

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

Java中的RASP

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

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

  上图的流程为: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源码:https://github.com/linxin26/javarespdemo

文章首发地址:Solinx

http://www.solinx.co/archives/950

Java中的RASP实现的更多相关文章

  1. Java中的Unsafe在安全领域的一些应用总结和复现

    目录 0 前言 1 基本使用 1.1 内存级别修改值 1.2 创建对象 1.3 创建VM Anonymous Class 2 利用姿势 2.1 修改值以关闭RASP等防御措施 2.2 创建Native ...

  2. RASP | 远程Java应用的RASP调试教程

    远程Java应用的RASP调试教程 介绍 Java RASP是基于Java Agent技术实现的,而Java Agent代码无法独立启动,必须依赖于一个Java运行时程序才能运行. 如何调试一个Jav ...

  3. java中的锁

    java中有哪些锁 这个问题在我看了一遍<java并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够.于是再次翻看了一下书里的内容,突然有点打开脑门的感觉.看来确实是要学习的最好方式 ...

  4. java中的字符串相关知识整理

    字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...

  5. Java中的Socket的用法

                                   Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的 ...

  6. java中Action层、Service层和Dao层的功能区分

    Action/Service/DAO简介: Action是管理业务(Service)调度和管理跳转的. Service是管理具体的功能的. Action只负责管理,而Service负责实施. DAO只 ...

  7. Java中常用集合操作

    一.Map 名值对存储的. 常用派生类HashMap类 添加: put(key,value)往集合里添加数据 删除: clear()删除所有 remove(key)清除单个,根据k来找 获取: siz ...

  8. java中的移位运算符:<<,>>,>>>总结

    java中有三种移位运算符 <<      :     左移运算符,num << 1,相当于num乘以2 >>      :     右移运算符,num >& ...

  9. 关于Java中进程和线程的详解

    一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...

随机推荐

  1. Java基础知识➣泛型整理(四)

    概述 泛型的本质是参数化类型,使用同一套代码来满足不同数据类型的业务需要,提高代码的执行效率,使代码简单明了. 泛型方法 该方法在调用时可以接收不同类型的参数.根据传递给泛型方法的参数类型,编译器适当 ...

  2. 【bzoj4631】踩气球 线段树

    题解: 真是很zz 我都想到线段树分治的思路了... 不过还是一道好题 首先跟线段树分治一样将区间投射到线段树上去 每次修改如果该个区间修改为0,则对他们对应的特定区间-1 这样每个区间会有一次变0, ...

  3. Linux 记录所有用户登录和操作的详细日志

    1.起因 最近Linux服务器上一些文件呗篡改,想追查已经查不到记录了,所以得想个办法记录下所有用户的操作记录. 一般大家通常会采用history来记录,但是history有个缺陷就是默认是1000行 ...

  4. Python之抽象类、抽象方法

    抽象类中只能有抽象方法,子类继承抽象类时,不能通过实例化使用其抽象方法,必须实现该方法. Python2 class CopyBase(object): def save(self): raise N ...

  5. 用面向对象重写thread 实现多次调用一个线程

    思路: 利用thread类中,run方法在子线程中调用,其他方法在主线程调用,所以将生产者写入主线程,将消费者写入run函数中在子线程中执行,完成生产者消费者模型 注意: 1. 要在 init 函数中 ...

  6. bootstrapTable

    一个详细的教程 table参数 bootstrap table使用总结 BootstrapTable使用实例 事件event 事件函数的用法: 方法1 $('#table').bootstrapTab ...

  7. Service插件化解决方案

    --摘自<android插件化开发指南> 1.ActivityThread最终是通过Instrumentation启动一个Activity的.而ActivityThread启动Servic ...

  8. CISCO ACL配置(目前)

    什么是ACL? 访问控制列表简称为ACL,访问控制列表使用包过滤技术,在路由器上读取第三层及第四层包头中的信息如源地址,目的地址,源端口,目的端口等,根据预先定义好的规则对包进行过滤,从而达到访问控制 ...

  9. Typescript中抽象类与接口详细对比与应用场景介绍

    现如今,TS正在逐渐成为前端OO编程的不二之选,以下是我在学习过程中对抽象类和接口做的横向对比. 1. 抽象类当做父类,被继承.且抽象类的派生类的构造函数中必须调用super():接口可以当做“子类” ...

  10. typescript精简版1:用ts表示常见数据类型

    一:工程准备: 1.全局安装typescript npm i typescript -g // 或 yarn global add typescript vscode 配置 在命令行执行 tsc -- ...