Java中的RASP实现
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实现的更多相关文章
- Java中的Unsafe在安全领域的一些应用总结和复现
目录 0 前言 1 基本使用 1.1 内存级别修改值 1.2 创建对象 1.3 创建VM Anonymous Class 2 利用姿势 2.1 修改值以关闭RASP等防御措施 2.2 创建Native ...
- RASP | 远程Java应用的RASP调试教程
远程Java应用的RASP调试教程 介绍 Java RASP是基于Java Agent技术实现的,而Java Agent代码无法独立启动,必须依赖于一个Java运行时程序才能运行. 如何调试一个Jav ...
- java中的锁
java中有哪些锁 这个问题在我看了一遍<java并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够.于是再次翻看了一下书里的内容,突然有点打开脑门的感觉.看来确实是要学习的最好方式 ...
- java中的字符串相关知识整理
字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...
- Java中的Socket的用法
Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的 ...
- java中Action层、Service层和Dao层的功能区分
Action/Service/DAO简介: Action是管理业务(Service)调度和管理跳转的. Service是管理具体的功能的. Action只负责管理,而Service负责实施. DAO只 ...
- Java中常用集合操作
一.Map 名值对存储的. 常用派生类HashMap类 添加: put(key,value)往集合里添加数据 删除: clear()删除所有 remove(key)清除单个,根据k来找 获取: siz ...
- java中的移位运算符:<<,>>,>>>总结
java中有三种移位运算符 << : 左移运算符,num << 1,相当于num乘以2 >> : 右移运算符,num >& ...
- 关于Java中进程和线程的详解
一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...
随机推荐
- [转] 浅析JavaScript设计模式——发布-订阅/观察者模式
前一段时间一直在写CSS3的文章 一直都没写设计模式 今天来写写大名鼎鼎观察者模式 先画张图 观察者模式的理解 我觉得还是发布-订阅模式的叫法更容易我们理解 (不过也有的书上认为它们是两种模式……) ...
- 标准C语言实现基于TCP/IP协议的文件传输
TCP/IP编程实现远程文件传输在LUNIX中一般都采用套接字(socket)系统调用. 采用客户/服务器模式,其程序编写步骤如下: 1.Socket系统调用 为了进行网络I/O,服务器和客户机两 ...
- 萨塔尼亚的期末考试(fail)
题解: 这题比较妙啊... 首先暴力自己算是找不出规律的 有一种直觉就是可以把式子变成{f[1]+...f[n]}+{f[2]+...+f[n]}+{f[3]+...+f[n]}... 然后看了题解发 ...
- mongodb中投票节点作用
投票节点 并不含有 复制集中的数据集副本,且也 无法 升职为主节点.投票节点的存在是为了使复制集中的节点数量为奇数,这样保证在进行投票的时候不会出现票数相同的情况.如果添加了一个节点后,总节点数为偶数 ...
- Codeforces 989D A Shade of Moonlight
A Shade of Moonlight 列列式子发现, 对于同一个云来说只有反向的云才能和它相交, 然后我们发现这个东西有单调性,然后二分就好啦. #include<bits/stdc++.h ...
- 实现 js 数据类型的判断函数type
type = (obj) => { const pass1 = typeof obj if (pass1 != 'object') return pass1 const pass2 = obj ...
- BZOJ2821 作诗(Poetize) 主席树 bitset
原文链接https://www.lydsy.com/JudgeOnline/problem.php?id=2821 题目传送门 - BZOJ2821 题意 $n$ 个数,$m$ 组询问,每次问 $[l ...
- 使用aws中国的s3时,制订bucket poicy时注意注意……
{ "Version": "2012-10-17", "Statement": [ { "Sid": "Pub ...
- js扩展运算符(spread)是三个点(...)
作用:将一个数组转为用逗号分隔的参数序列. //该运算符主要用于函数调用.function push(array, ...items) { array.push(...items); } functi ...
- HDU 1710 (二叉树的前序和中序,求后序)
题目链接 题目大意: 输入二叉树的前序.中序遍历,请输出它的后序遍历 #include <stdio.h> #include <string.h> ; // 长度为n s1 前 ...