在学习Java安全之前首先对Java开发的基础知识简单学习
因为Java体系比较庞大,没有学过Java开发的初学者很容易搞晕,介绍部分常用技术
简单了解下即可,之后会经常打交道
Maven是一个项目构建工具,可以对Java项目进行构建和管理,也可以用于各种项目的构建和管理。Maven采用了Project Object Model(POM)概念来管理项目。IDEA中内置有Maven,对于并非专业开发者的安全人员,内置的Maven即可满足大多数需求
pom.xml定义依赖关系
pom.xml文件中的dependencies和dependency用于定义依赖关系,dependency通过groupId、artifactId以及version来定义所依赖的项目
使用
IDEA中可以在新建项目时选择创建Maven项目,创建完成的Maven项目中包含该pom.xml文件。pom.xml文件描述了项目的Maven坐标、依赖关系、开发者需要遵循的规则、缺陷管理系统、组织以及licenses,还有其他所有的项目相关因素。对于安全人员来说,可以从pom.xml文件中审查当前Java应用程序是否使用了存在安全隐患的组件,以及快速搭建特定版本的漏洞环境
在代码审计过程中,例如搭建Fastjson 1.24之前版本的反序列化漏洞环境时,需要引入版本小于1.24的Fastjson组件,如前所述使用Maven搭建相应的环境,在pom.xml文件中填入Fastjson的项目通用名称、项目版本等信息
然后右键单击pom.xml文件选择“Maven”选项,并单击“Reimport”按钮,即可进行组件的自动获取
MVC 全名是 Model ViewController,M(Model)是指数据模型,V(View)是指用户界面,C(Controller)是控制器。使用 MVC 最直接的目的就是将M和V实现代码分离,C则是确保 M 和 V 的同步,一旦 M 改变,V就应该同步更新。简单来说,MVC是一个设计模式,它强制性地使应用程序的输入、处理和输出分开。MVC应用程序被分成3个核心部件:Model、View、Controller。它们独立处理各自的任务
Java MVC模式与普通 MVC 的区别不大,具体如下
工作流程
首先,Controller层接收用户的请求,并决定应该调用哪个Model来进行处理;然后,由Model使用逻辑处理用户的请求并返回数据;最后,返回的数据通过View层呈现给用户
Java MVC框架也有很多
Servlet其实是在 Java Web容器中运行的小程序。用户通常使用 Servlet 来处理一些较为复杂的服务器端的业务逻辑,Servlet 是 Java EE的核心,也是所有MVC框架实现的根本
版本不同,Servlet的配置不同。Servlet 3.0之前的版本都是在web.xml中配置的,而Servlet 3.0之后的版本则使用更为便捷的注解方式来配置。此外,不同版本的Servlet所需的Java/JDK版本也不相同
其他内容可以看上篇文章:Javaweb-Servlet总结
项目部署和运行可以看这篇:Javaweb-初识
filter被称为过滤器,是 Servlet 2.3新增的一个特性,同时它也是Servlet 技术中最实用的技术。开发人员通过Filter技术,能够实现对所有Web资源的管理,如实现权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能
filter的配置类似于Servlet,由和两组标签组成,同样,如果Servlet版本大于3.0,也可以使用注解的方式来配置filter
1.xml配置
2.注解配置
添加@WebServlet注解就可以修改filter的属性
第15行的@WebServlet的注解参数有description及urlPatterns,此外还有很多
一般不推荐使用注解方式来配置 filter,因为如果存在多个过滤器,使用 web.xml配置filter可以控制过滤器的执行顺序;如果使用注解方式来配置 filter,则无法确定过滤器的执行顺序
filter接口中有一个doFilter方法,当开发人员编写好Filter的拦截逻辑,并配置对哪个Web资源进行拦截后,Web服务器会在每次调用Web资源的service() 方法之前先调用doFilter方法
filter 进行拦截的方式也很简单,在 HttpServletRequest 到达Servlet之前,filter拦截客户的HttpServletRequest,根据需要检查 HttpServletRequest,也可以修改HttpServletRequest头和数据。在HttpServletResponse到达客户端之前,拦截HttpServletResponse,根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据
接口方法
与Servlet 接口不同的是,filter接口在创建时就默认创建了所有的方法
Init() 接口
filter中的init() 方法用于初始化过滤器
public void init(FilterConfig fConfig) throws ServletException{
// 此处为开发者定义的初始化代码
}
开发者可以在 init() 方法中完成与构造方法类似的初始化功能。如果初始化代码中要用到FillerConfig 对象,则这些初始化代码只能在 filler 的 init() 方法中编写,而不能在构造方法中编写
doFilter() 接口
doFilter 方法类似于 Servlet 接口的 service() 方法,当客户端请求目标资源时,容器会筛选出符合 标签中 的 filter,并按照声明 的顺序依次调用这些 filter 的 doFilter() 方法
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOExecption,ServletException{
// 开发者定义的过滤代码
// 传递filter链
chain.doFilter(request,response);
}
destroy() 接口
filter 中的destroy() 方法与 Servlet 中的destroy() 作用类似,在 Web 服务器卸载filter 对象之前被调用,用于释放被 filter 对象打开的资源,如关闭数据库、关闭I/O 流等
public void destory(){
// 定义的终止操作代码
}
filter 的生命周期与Servlet的生命周期比较类似
反射(Refection)是Java的特征之一
通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。同样,Java的反射机制也是如此,在运行状态中,通过 Java 的反射机制,我们能够判断一个对象所属的类;了解任意一个类的所有属性和方法;能够调用任意一个对象的任意方法和属性。这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制
在开发过程中使用Eclipse、IDEA等开发工具时,当我们输入一个对象或类并想调用它的属性或方法时,编译器会自动列出它的属性或方法,这是通过反射实现的;再如,JavaBean和JSP之间的调用也是通过反射实现的。反射最重要的用途是开发各种通用框架,如上文中提到的Spring框架以及ORM框架,都是通过反射机制来实现的
获取类对象
Class.forName();
直接获取
任何数据类型都具备静态的属性,因此可以使用.class直接获取其对应的Class对象。这种方法相对简单,但要明确用到类中的静态成员
可以通过 Object 类中的 getClass() 方法来获取字节码对象,必须要明确具体的类,然后创建对象
获取类方法
获取类成员变量
利用Java的反射机制,我们可以无视类方法、变量访问权限修饰符,调用任何类的任意方法、访问并修改成员变量值,但是这样做可能导致安全问题,之后会用到
Java程序是由class文件组成的一个完整的应用程序。在程序运行时,并不会一次性加载所有的class文件进入内存,而是通过Java的类加载机制(ClassLoader)进行动态加载,从而转换成java.lang.Class 类的一个实例
ClassLoader类
ClassLoader是一个抽象类,主要的功能是通过指定的类的名称,找到或生成对应的字节码,返回一个java.lang.Class 类的实例。开发者可以继承ClassLoader类来实现自定义的类加载器
ClassLoader类中和加载类相关的方法
loadClass()方法流程
loadClass()方法可以加载类并返回一个java.lang.Class类对象,
当loadClass()方法被调用时,会首先使用findLoadedClass()方法判断该类是否已经被加载,如果未被加载,则优先使用加载器的父类加载器进行加载。当不存在父类加载器,无法对该类进行加载时,则会调用自身的findClass()方法,因此可以重写findClass()方法来完成一些类加载的特殊要求
通过重写findClass()方法,利用defineClass()方法来将字节码转换成java.lang.class类对象,就可以实现自定义的类加载器
loadClass()方法与Class.forName的区别
loadClass()方法只对类进行加载,不会对类进行初始化。Class.forName会默认对类进行初始化。当对类进行初始化时,静态的代码块就会得到执行,而代码块和构造函数则需要适合的类实例化才能得到执行
URLClassLoader
URLClassLoader类是ClassLoader的一个实现,拥有从远程服务器上加载类的能力。通过URLClassLoader可以实现对一些WebShell的远程加载、对某个漏洞的深入利用
动态代理
代理是 Java中的一种设计模式,主要用于提供对目标对象另外的访问方式,即通过代理对象访问目标对象。这样,就可以在目标对象实现的基础上,加强额外的功能操作,实现扩展目标对象的功能,在Java反序列化利用中经常使用
代理模式的关键点在于代理对象和目标对象,代理对象是对目标对象的扩展,并且代理对象会调用目标对象
Java 代理的方式有3种:
用于Java Web的安全开发框架
Spring Security
Spring 是一个非常成功的 Java 应用开发框架。Spring Security 基于 Spring 框架,提供了一套Web 应用安全性的完整解决方案,它能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案
Spring Security 提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC(Inversion of Control , 控制反转)、DI(Dependency Injection , 依赖注入)和AOP(AspectOriented Programming , 面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作
Apache Shiro
Apache Shiro也是一个强大的Java安全框架,该框架能够用于身份验证、授权、加密和会话管理。与Spring Security 框架相同,Apache Shiro也是一个全面的、蕴含丰富功能的安全框架
在 Apache Shiro 框架中,开发团队提供了4个重点安全配置:Authentication(认证)、Authorization(授权)、Session Management(会话管理)、Cryptography(加密)
OAuth
OAuth(Open Authorization,开放授权)为用户资源的授权定义了一个安全、开发以及简单的标准,第三方无须知道用户的账号和密码,即可获取用户的授权信息。OAuth 2.0 是OAuth协议的延续版本,但是并不兼容OAuth 1.0
与Spring Security 和 Apache Shiro 两者相比,OAuth 2.0并非是一个Java Web 框架,而是一个用于授权的行业标准协议
JWT
JSON Web Token(JWT)是一个开放标准(RFC7519),它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象的形式安全地传输信息。与OAuth 2.0 不同,JWT 是一种具体的 token实现框架,而 OAuth 2.0 是一种授权协议,是规范,并不是实现。JWT比较适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其他业务逻辑所必需的声明信息。该token也可以直接用于认证,也可以被加密
IntelliJ IDEA可以在无源代码的情况下进行远程调试,只需将程序的class文件或Jar包添加进项目依赖即可对一些未开源的Java程序或大型中间件进行远程调试
对jar包进行远程调试
以一个 2021东华杯web1 题目附件为例,冰蝎也可以
反编译jar包
使用IntelliJ IDEA创建一个Java项目,并创建一个lib文件夹将Jar包放入,选中lib文件夹后,右键选择“Add as Library…”,将lib文件夹添加进项目依赖
添加成功后idea会自动反编译,就可以看到代码了
配置远程环境
通过右上角的“Add Configurations”,并单击“+”来添加一个“Remote”(idea2021中是Remote JVM Debug)
下图为默认配置,单击“Apply”提交并保存即可
其中“-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005”将作为运行时的启动参数
将“-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005”作为启动参数运行Jar包(注意启动目录)
单击IntelliJ IDEA右上角的Debug按钮,即可发现程序在断点处暂停,然后就可以进行逐步的调试了
不仅如此还可以使用 idea 调试 docker 中的 weblogic 和 tomcat,参考《Java代码审计入门篇》
关于Java代码审计入门篇这本书可以去微信读书看