JAVA代码审计(三)-基础知识

一、Java 三层架构

Java 项目开发过程中把整个项目分为三层:表现层、数据访问层、业务逻辑层。

  • 表现层:MVC(Model-ViewControler)模式,采用 JSP/Servlet 技术进行页面效果显示,即用户操作界面,为用户提供交互操作。
  • 数据访问层: 采用 DAO 模式, DAO 为提供访问关系型数据库系统所需操作的接口,将数据访问和业务逻辑分离对上层提供面向对象的数据访问接口,也就是哪个类对应哪个表,哪个属性对应哪个列,为业务逻辑层提供数据,根据传入的值来操作数据库,增、删、改、查。
  • 业务逻辑层:采用事务脚本模式,负责关键业务的处理和数据传递,复杂的逻辑判断和涉及到数据库的数据验证均需要在此进行处理,根据用户传入的值返回相关数据或处理相关逻辑。

SSH 框架

1
2
3
业务层——Spring
表现层——Struts
持久层——Hibernate

SSM框架

1
2
3
业务层——Spring
表现层——SpringMVC
持久层——MyBatis

二、MVC模式

MVC,全称是 Model-View-Controller(模型-视图-控制器),是一种设计的规范,一种架构的模式。

1
2
3
Model 模型:提供数据和行为,即 Dao 和 Service
View 视图: GUI组件,用于数据的展示,及jsp、html等
Controller 控制器:用于流程的控制,接收用户请求,交给业务层处理,再把处理后的模型数据响应给视图,即servlet、controller

工作流程

1
Controller层接收到用户的请求,并决定应该用哪个Model来进行处理,然后由 Model 使用逻辑处理用户的请求并返回数据;最后,返回的数据通过View层呈现给用户。

三、Maven

Maven 是一个项目管理工具,它包含了一个对象模型(Project Object Model),是一组标准集合,一个依赖管理系统,用来运行定义在生命周期阶段中插件目标和逻辑。核心功能为:通过 pom.xml文件的配置获取 jar 包不用手动去添加 jar 包。日常项目开发需要应用各种jar包,少则几十、多至上百,若每一个jar都使用人工导入则大大增加工作量。Maven使这一部分工作变得简单,开发者只需通过定义 pom.xml,maven 即可调用仓库的 jar 包直接导入。


四、pom.xml

POM 是项目对象模型 (Project Object Model) 的简称,它是 Maven 项目中的文件,使用XML表示,名称叫做pom.xml。该文件用于管理源代码、配置文件、开发者的信息和角色、问题追踪系统、组织信息、项目授权、项目的url、项目的依赖关系等。Maven 项目中必须包含 pom.xml 文件。例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?xml version="1.0" encoding="UTF-8"?>
<!--project 标签:根标签,表示对当前工程进行配置、管理-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!--modelVersion 标签:从Maven2开始就固定是4.0.0-->
<!--代表当前pom.xml所采用的标签结构-->
<modelVersion>4.0.0</modelVersion>

<!--坐标信息-->
<!--在本地仓库中安装后对应的路径:com\seer\maven\seer-heathy-demo\1.0-SNAPSHOT\seer-heathy-demo-1.0-SNAPSHOT.jar-->
<!--groupId 标签:坐标向量之一;代表公司或组织开发的某一个项目-->
<groupId>com.seer.maven</groupId>
<!--artifactId 标签:坐标向量之一;代表项目下的某一个模块-->
<artifactId>seer-heathy-demo</artifactId>
<!--version 标签:坐标向量之一;代表当前模块的版本-->
<version>1.0-SNAPSHOT</version>

<!--packaging 标签:打包方式-->
<!--取值jar:生成jar包,说明这是一个Java工程-->
<!--取值war:生成war包,说明这是一个Web工程-->
<!--取值pom:说明这个工程是用来管理其他工程的工程-->
<packaging>jar</packaging>
<name>seer-heathy-demo</name>
<url>http://maven.apache.org</url>

<!--在Maven中定义属性值-->
<properties>
<!--在构建过程中读取源码时使用的字符集-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<!--dependencies标签:配置具体依赖信息,可以包含多个dependency子标签-->
<dependencies>
<!--dependency 标签:配置一个具体的依赖信息-->
<dependency>
<!--坐标信息:导入那个jar包,就配置它的坐标信息即可-->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<!--scope标签:配置当前依赖的范围-->
<scope>test</scope>
</dependency>
</dependencies>


</project>

五、Spring Boot

SpringBoot 是一款基于 JAVA 的开源框架。目的是为了简化 Spring 应用搭建和开发流程。现在绝大多数的新项目都基于 Spring Boot 的 Spring MVC 来实现。

1、Spring MVC

Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet(Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。)

2、接口

  • DispatcherServlet 接口:前端控制器,所有请求都经过它来统一分发,DispatcherServlet将请求分发给Spring Controller之前,需要借助于Spring提供的HandlerMapping定位到具体的 Controller。
  • HandlerMapping 接口:完成客户请求到 Controller 的映射。
  • Controller接口:并发用户处理 DispatcherServlet、HandlerMapping 请求,因此实现Controller接口时,必须保证线程安全并且可重用。

3、DispatcherServlet

是整个Spring MVC的核心。主要负责截获请求并将其分派给相应的处理器处理。其主要工作有以下三项:

  • 截获符合特定格式的URL请求
  • 初始化 DispatcherServlet 上下文对应 WebApplicationContext ,并将其与业务层、持久化层的 WebApplicationContext 建立关联。
  • 初始化 Spring MVC 的各个组成组件,并装配到 DispatcherServlet 中。

六、Java 反射机制

Java 反射机制可以无视类方法、变量去访问权限修饰符(如 protected、private等),并且可以调用任何类的任意方法、访问并修改成员变量值。即当我们在能控制反射的类名、方法名、参数的前提下,发现了一处 Java 反射调用的漏洞,我们就可以任意构造攻击。

1、什么是反射
反射使 Java 代码能够发现有关已加载类的字段、方法和构造函数的信息,并在安全限制内使用反射的字段、方法和构造函数对底层对应的对象进行操作。即通过反射我们可以在运行时获得程序或程序集中每一个类型的成员和成员信息。在运行状态中通过反射机制,我们能判断一个对象所属的类,了解任意一个类的所有属性和方法,能够调用任意一个对象的任意方法和属性。

2、不安全的反射机制
Java 的反射机制可以无视类方法、变量访问权限修饰符,可以调用任何类的任意方法、访问并修改成员变量值,那么这可能导致安全问题,如果一个攻击者能够通过应用程序创建意外的控制流路径,那么就有可能绕过安全检查发起相关攻击。假设有段代码如下:

1
2
3
4
5
6
7
8
9
10
String name = request.getParameter("name");
Command command = null;
if (name.equals("Delect")) {
command = new DelectCommand();
} else if (ctl.equals("Add")) {
command = new AddCommand();
} else {
...
}
command.doAction(request);

存在一个字段为 name,当获取用户请求的 name 字段后进行判断,如果请求的是 Delect 操作,则执行DelectCommand 函数,若执行的是 Add 操作,则执行 AddCommand 函数,如果不是这两种操作,则执行其他代码。
此时,假如有位开发者看到了这段代码,他觉得可以使用Java 的反射来重构此代码以减少代码行,如下所示:

1
2
3
4
String name = request.getParameter("name");
Class ComandClass = Class.forName(name + "Command");
Command command = (Command) CommandClass.newInstance();
command.doAction(request);

这样的重构看起来使得代码行减少,消除了 if/else 块,而且可以在不修改命令分派器的情况下添加新的命令类型,但是如果没有对传入进来的 name 字段进行限制,那么我们就能实例化实现 Command 接口的任何对象,从而导致安全问题。实际上,攻击者甚至不局限于本例中的 Command 接口对象,而是使用任何其他对象来实现,如调用系统中任何对象的默认构造函数,再如调用 Runtime 对象去执行系统命令,这就可能导致远程命令执行漏洞,因此不安全的反射的危害性极大,也是我们审计过程中重点关注的内容。


七、Java EE 核心技术

  • JDBC:Java 数据库链接,提供了客户端访问数据库应用的程序接口以及查询和更新数据库中数据的方法。
  • JNDI:Java 命名和目录接口,是 JAVA 的一个目录服务应用程序界面,提供了一个目录系统,并将服务名称与对象关联起来,使开发者在开发过程中可以用名称来访问对象。
  • RMI:远程调用方法,为 Java 的一组拥护开发分布式应用程序的 API,增强了Java 开发分布式应用的能力。
  • Servlet:Java Web 容器中运行的程序,通常用来处理较为复杂的服务端业务逻辑。使用 Java 编写而成的服务器程序,狭义的 Servlet 为 Java 语言实现的一个接口,广义的说就是任何实现该 Servlet 接口的类,主要功能在于交互式地浏览和修改数据,生成动态的 web 内容。

八、Java 基础语法

1、前言

Java 是一个解释型的语言,可跨平台执行。一个 Java 程序可以认为是一系列对象的集合,这些对象通过调用彼此的方法来协同工作,每个 Java 文件都是一个类,同一文件夹下可以互相调用其他文件下的类,不同文件夹下可以通过 import 命令进行导入。其中 Java 中的包其实就是文件夹,在同一个包中可以理解成同一文件夹下。

2、语法注意点

注意点 简述
大小写敏感 Java 是大小写敏感的,这就意味着标识符 Hello 与 hello 是不同的
类名 对于所有的类来说,类名的首字母应该大写。如果类名由若干单词组成,那么每个单词的首字母应该大写,例如 MyFirstJavaClass
方法名 所有的方法名都应该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写
源文件名 源文件名必须和类名相同。当保存文件的时候,你应该使用类名作为文件名保存(切记 Java 是大小写敏感的),文件名的后缀为 .java。(如果文件名和类名不相同则会导致编译错误)
主方法入口 所有的 Java 程序由 public static void main(String[] args) 方法开始执行

3、简单实例

1
2
3
4
5
6
7
8
public class HelloWorld {
/* 第一个Java程序
* 它将输出字符串 Hello World
*/
public static void main(String[] args) {
System.out.println("Hello World"); // 输出 Hello World
}
}

image-20220924211209757


4、构造函数

一个类可以包含以下类型变量:

  • 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
  • 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
  • 类变量:类变量也声明在类中,方法体之外,但必须声明为 static 类型。

一个类可以有多个构造函数,根据对象的创建情况触发不同的构造函数,若在创建对象的时候传入了参数,则会触发第二个构造函数,如果不传入参数则会触发第一个构造函数,例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class test{
String name = "cody";
public test(){
System.out.println("Other class named test");
}
}

public class demo1 {
public demo1()
{
System.out.println("The first Construct def.");
}
public demo1(String a)
{
System.out.println("The second Construct def,string is "+a);
}

public static void main(String[] args) {
demo1 test = new demo1("SFL");
demo1 test2 = new demo1();
test te = new test();
System.out.println(te.name);
}
}

运行结果如下:

image-20221009145926209


5、类与对象

假设有一个类名为Test,创建对象abc,语法如下

1
2
// 类名称 对象名 = new 类名称();
test abc = new test();

类:类是一个模板,它描述一类对象的行为和状态。例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Dog {
String breed;
int size;
String colour;
int age;

void eat() {
}

void run() {
}

void sleep(){
}

void name(){
}
}

对象:对象是类的一个实例。例子如下:

  • 声明:声明一个对象,包括对象名称和对象类型。
  • 实例化:使用关键字 new 来创建一个对象。
  • 初始化:使用 new 创建对象时,会调用构造方法初始化对象。
1
2
3
4
5
6
7
8
9
10
public class Puppy{
public Puppy(String name){
//这个构造器仅有一个参数:name
System.out.println("小狗的名字是 : " + name );
}
public static void main(String[] args){
// 下面的语句将创建一个Puppy对象
Puppy myPuppy = new Puppy( "tommy" );
}
}

6、变量与常量

例子如下,a为变量,b在char前加了 final 为常量,常量无法被更改。

1
2
char a = 'a';
final char b = 'b';

7、访问实例变量和方法

通过已创建的对象来访问成员变量和成员方法,如下所示:

1
2
3
实例化对象:Object referenceVariable = new Constructor();
访问类中的变量:referenceVariable.variableName;
访问类中的方法:referenceVariable.methodName();

实例,下面的例子展示如何访问实例变量和调用成员方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Puppy{
int puppyAge;
public Puppy(String name){
// 这个构造器仅有一个参数:name
System.out.println("小狗的名字是 : " + name );
}

public void setAge( int age ){
puppyAge = age;
}

public int getAge( ){
System.out.println("小狗的年龄为 : " + puppyAge );
return puppyAge;
}

public static void main(String[] args){
/* 创建对象为myPuppy */
Puppy myPuppy = new Puppy( "tommy" );
/* 利用创建的对象,通过方法来设定age */
myPuppy.setAge( 2 );
/* 调用另一个方法获取age */
myPuppy.getAge( );
/*你也可以像下面这样访问成员变量 */
System.out.println("变量值 : " + myPuppy.puppyAge );
}
}

运行结果如下:

image-20221009150647117


8、接口

接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

接口的声明语法格式如下:

1
2
3
4
[可见度] interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}

Interface关键字用来声明一个接口。下面是接口声明的一个简单例子。

1
2
3
4
5
6
7
8
9
/* 文件名 : NameOfInterface.java */
import java.lang.*;
//引入包

public interface NameOfInterface
{
//任何类型 final, static 字段
//抽象方法
}

实例,Animal.java 文件代码:

1
2
3
4
5
/* 文件名 : Animal.java */
interface Animal {
public void eat();
public void travel();
}

接口的实现,当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* 文件名 : MammalInt.java */
public class MammalInt implements Animal{

public void eat(){
System.out.println("Mammal eats");
}

public void travel(){
System.out.println("Mammal travels");
}

public int noOfLegs(){
return 0;
}

public static void main(String args[]){
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}

以上实例编译运行结果如下:

1
2
Mammal eats
Mammal travels

九、参考文章

https://blog.csdn.net/chehec2010/article/details/123997607
https://blog.csdn.net/jianyuerensheng/article/details/51258942
https://www.freebuf.com/vuls/344685.html
https://www.cnpanda.net/codeaudit/705.html
https://blog.csdn.net/qq_41874930/article/details/115614958