漏洞信息

CWE编号:CWE-476
空指针解引用,NULL Pointer Dereference

概念解析

刚开始我看到这个缺陷也是一脸懵,实际查资料之后才发现:哦!原来就是这玩意儿啊!
在搞清楚啥叫空指针解引用之前,我们应该明确组成这个词的2个基本概念所代表的含义:
1、空指针(Null Pointer)
Java不能直接操作指针,其数据类型可以分为基本类型(byte、short、int、long、float、double、char、boolean)和引用类型,基础类型直接存储实际的值,引用类型存储的是对象的引用,而不是对象本身,相当于C/C++中的指针。
在Java中,我们可以将空指针简单理解引用的对象不存在,返回地址为null

2、解引用
这是理解这个缺陷的最大障碍,很多人因为分不清楚引用(Reference)解引用(Dereference) 的区别,导致对此缺陷不理解。
我们通过一个例子简单说明二者的区别

1
2
3
4
5
// 引用
String str = "Hello, World!";

// 解引用
System.out.println(str.length());

引用本身不会直接访问对象内容,只是一个“指向”的概念;(上述例子中声明了一个str变量)
通过引用去访问对象的内容(属性、方法等)的过程称为解引用。(上述例子中调用了length()方法)

明确了上述两个概念之后,我们就很容易理解什么叫空指针解引用了。
在访问对象内容(解引用)的时候,如果无法确保引用是有效的(非 null),程序就会抛出空指针异常NullPointerException。

修复方案&案例

修复方案非常简单:在使用引用类型的数据前进行判空操作

eg1: 空指针解引用

1
2
3
4
5
6
7
8
9
10
String str = null; // 引用是 null
//不合规示例
System.out.println(str.length()); // 解引用 null,抛出 NullPointerException

//合规示例
if (str != null) {
System.out.println(str.length()); // 安全解引用
} else {
System.out.println("String is null, cannot get length.");
}

eg2: 解引用未初始化的对象

不合规代码示例:

1
2
3
4
5
6
7
8
9
10
public class UninitializedObjectExample {
static class Person {
String name;
}

public static void main(String[] args) {
Person person = null; // 引用未指向任何实际对象
System.out.println(person.name); // 解引用 null,抛出 NullPointerException
}
}

修复方案:确保在使用前正确初始化对象

1
2
3
4
5
6
7
8
9
10
public class UninitializedObjectExample {
static class Person {
String name = "Default Name"; // 初始化字段
}

public static void main(String[] args) {
Person person = new Person(); // 确保对象被正确初始化
System.out.println(person.name); // 安全解引用
}
}

eg3: 解引用已释放的资源

不合规代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ClosedResourceExample {
public static void main(String[] args) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("example.txt"));
reader.close(); // 释放资源
System.out.println(reader.readLine()); // 解引用已关闭的资源,抛出 IOException
} catch (IOException e) {
e.printStackTrace();
}
}
}

合规代码:通过try-with-resources 确保资源在使用后自动关闭,避免手动操作的错误。

PS:try-with-resources是Java7中引入的资源管理机制,用于在代码执行完毕后自动关闭实现了 AutoCloseable 接口的资源(如文件、数据库连接、网络流等)。资源声明在 try 的括号中,当 try 块执行完毕(无论是否抛出异常),资源会被自动关闭(调用 close() 方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ClosedResourceExample {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
System.out.println(reader.readLine()); // 安全解引用
} catch (IOException e) {
e.printStackTrace();
}
// reader 自动关闭,无需手动关闭,避免解引用已释放资源
}
}

审计思路

推荐工具审计。
人工审计的方式就比较笨,得花时间一个个看,难度很低,但没有必要。

参考链接

  1. Java 空指针在检查后解引用_mob64ca12d78ba3的技术博客_51CTO博客
  2. 深入理解 Java try-with-resource 语法糖深入理解 Java try-with-resource 语 - 掘金