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中进程和线程的详解
一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...
随机推荐
- python---使用递归实现谢尔宾斯基三角形及汉诺塔
渐入佳境. # coding: utf-8 import turtle ''' # =================turtle练手== def draw_spiral(my_turtle, lin ...
- jQuery分步步骤
插件描述:jQuery上一步.下一步,分步步骤,兼容性如下: 使用方法 1.引入样式和脚本 <link rel="stylesheet" type="text/cs ...
- javascript获取时间戳
时间戳: 时间戳是自 1970 年 1 月 1 日(00:00:00 GMT)以来的秒数.它也被称为 Unix 时间戳(Unix Timestamp). JavaScript 获取当前时间戳: < ...
- Ng Alain使用 - cli和克隆两种方式
感觉没啥要写的,但是在查看相关资料的过程中发现不少浮夸的人,可以说是完全不阅读官方文档,操作完全无厘头,,创建了删,配置,再删除,,,扯蛋....., 方式一:CLI(推荐) # 确保使用的是最新版本 ...
- 开源堡垒机Gateone 安装过程记录及报错处理
1.下载git源码或者zip包都可以,下载到我们制定部署目录. git地址:https://github.com/liftoff/GateOne.git 2.检查tornado 版本,安装tornad ...
- 配置文件——节点<machineKey>的作用,强随机生成
<machineKey>这个节允许你设置用于加密数据和创建数字签名的服务器特定的密钥.ASP.NET自动使用它来保护表单验证Cookie,你也可以将它用于受保护的视图状态数据.同时,这个密 ...
- TypeScript学习笔记 (一)基础特性
一. 字符串新特性 1. 多行字符串 var str = ``; 2. 字符串模板 var world = "world"; var str = `hello ${world}`; ...
- ELK使用3-Logstash
一.命令行输入输出操作 1.命令行输出: /application/elk/logstash/bin/logstash -e 'input { stdin{} } output { stdout{} ...
- 2018牛客网暑假ACM多校训练赛(第五场)F take 树状数组,期望
原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round5-F.html 题目传送门 - https://www.no ...
- 亲和串 kmp
Problem Description 人随着岁数的增长是越大越聪明还是越大越笨,这是一个值得全世界科学家思考的问题,同样的问题Eddy也一直在思考,因为他在很小的时候就知道亲和串如何判断了,但是发现 ...