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. [转] 浅析JavaScript设计模式——发布-订阅/观察者模式

    前一段时间一直在写CSS3的文章 一直都没写设计模式 今天来写写大名鼎鼎观察者模式 先画张图 观察者模式的理解 我觉得还是发布-订阅模式的叫法更容易我们理解 (不过也有的书上认为它们是两种模式……)  ...

  2. 标准C语言实现基于TCP/IP协议的文件传输

    TCP/IP编程实现远程文件传输在LUNIX中一般都采用套接字(socket)系统调用. 采用客户/服务器模式,其程序编写步骤如下:  1.Socket系统调用  为了进行网络I/O,服务器和客户机两 ...

  3. 萨塔尼亚的期末考试(fail)

    题解: 这题比较妙啊... 首先暴力自己算是找不出规律的 有一种直觉就是可以把式子变成{f[1]+...f[n]}+{f[2]+...+f[n]}+{f[3]+...+f[n]}... 然后看了题解发 ...

  4. mongodb中投票节点作用

    投票节点 并不含有 复制集中的数据集副本,且也 无法 升职为主节点.投票节点的存在是为了使复制集中的节点数量为奇数,这样保证在进行投票的时候不会出现票数相同的情况.如果添加了一个节点后,总节点数为偶数 ...

  5. Codeforces 989D A Shade of Moonlight

    A Shade of Moonlight 列列式子发现, 对于同一个云来说只有反向的云才能和它相交, 然后我们发现这个东西有单调性,然后二分就好啦. #include<bits/stdc++.h ...

  6. 实现 js 数据类型的判断函数type

    type = (obj) => { const pass1 = typeof obj if (pass1 != 'object') return pass1 const pass2 = obj ...

  7. BZOJ2821 作诗(Poetize) 主席树 bitset

    原文链接https://www.lydsy.com/JudgeOnline/problem.php?id=2821 题目传送门 - BZOJ2821 题意 $n$ 个数,$m$ 组询问,每次问 $[l ...

  8. 使用aws中国的s3时,制订bucket poicy时注意注意……

    { "Version": "2012-10-17", "Statement": [ { "Sid": "Pub ...

  9. js扩展运算符(spread)是三个点(...)

    作用:将一个数组转为用逗号分隔的参数序列. //该运算符主要用于函数调用.function push(array, ...items) { array.push(...items); } functi ...

  10. HDU 1710 (二叉树的前序和中序,求后序)

    题目链接 题目大意: 输入二叉树的前序.中序遍历,请输出它的后序遍历 #include <stdio.h> #include <string.h> ; // 长度为n s1 前 ...