反序列化攻击,鹏磊最烦的就是这个。以前用全局过滤器,所有反序列化都用同一个规则,不够灵活。不同的上下文需要不同的安全策略,比如从网络接收的数据和从本地文件读取的数据,安全要求不一样。JDK 17 的 JEP 415 引入了上下文特定的反序列化过滤器,让你能为不同的反序列化上下文设置不同的过滤器,安全性提升了不少。
上下文特定的反序列化过滤器是 JEP 415 引入的特性,允许为不同的反序列化上下文设置不同的过滤器。这玩意儿提升了反序列化安全性,让你能根据不同的上下文(比如网络、文件、RMI)设置不同的安全策略,防止反序列化攻击。虽然配置可能稍微复杂一点,但安全性提升明显,值得投入。
什么是反序列化攻击
反序列化攻击是指攻击者通过构造恶意的序列化数据,在反序列化时执行恶意代码。常见的攻击方式包括:
- 代码执行:通过反序列化执行恶意代码
- 拒绝服务:通过构造大量数据导致内存溢出
- 信息泄露:通过反序列化获取敏感信息
// 反序列化攻击示例(危险,不要在生产环境使用)
// 攻击者可能构造恶意的序列化数据
// 在反序列化时执行恶意代码
// 导致系统被攻击
反序列化攻击是严重的安全威胁,需要有效防护。
为什么需要上下文特定的过滤器
以前使用全局过滤器,所有反序列化都用同一个规则,有几个问题:
- 不够灵活:不同上下文需要不同的安全策略
- 配置困难:全局配置难以满足所有场景
- 安全性差:一刀切的策略可能不够安全
上下文特定的过滤器解决了这些问题。
基础用法
上下文特定的过滤器基础用法:
import java.io.*;
import java.util.function.*;
// 创建上下文特定的过滤器
public class ContextSpecificFilter {
public static void main(String[] args) {
// 创建过滤器工厂
BinaryOperator<ObjectInputFilter> filterFactory = (current, next) -> {
// 根据上下文返回不同的过滤器
if (next != null) {
return next; // 使用下一个过滤器
}
return current; // 使用当前过滤器
};
// 设置过滤器工厂
ObjectInputFilter.Config.setSerialFilterFactory(filterFactory); // 设置工厂
// 创建网络过滤器(严格)
ObjectInputFilter networkFilter = ObjectInputFilter.Config.createFilter(
"java.base.**;!*" // 只允许 java.base 包
);
// 创建文件过滤器(宽松)
ObjectInputFilter fileFilter = ObjectInputFilter.Config.createFilter(
"java.**;!java.lang.Runtime" // 允许 java 包,但拒绝 Runtime
);
// 在网络上下文中使用严格过滤器
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(new byte[0]))) {
ois.setObjectInputFilter(networkFilter); // 设置网络过滤器
// 反序列化操作
} catch (IOException e) {
e.printStackTrace(); // 处理异常
}
// 在文件上下文中使用宽松过滤器
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(new byte[0]))) {
ois.setObjectInputFilter(fileFilter); // 设置文件过滤器
// 反序列化操作
} catch (IOException e) {
e.printStackTrace(); // 处理异常
}
}
}
上下文特定的过滤器让你能为不同上下文设置不同的安全策略。
过滤器工厂
过滤器工厂是上下文特定过滤器的核心:
import java.io.*;
import java.util.function.*;
// 过滤器工厂示例
public class FilterFactoryExample {
public static void main(String[] args) {
// 创建过滤器工厂
BinaryOperator<ObjectInputFilter> filterFactory = (current, next) -> {
// 合并当前过滤器和下一个过滤器
if (current == null && next == null) {
return null; // 没有过滤器
}
if (current == null) {
return next; // 只有下一个过滤器
}
if (next == null) {
return current; // 只有当前过滤器
}
// 合并两个过滤器
return ObjectInputFilter.merge(current, next); // 合并过滤器
};
// 设置过滤器工厂
ObjectInputFilter.Config.setSerialFilterFactory(filterFactory); // 设置工厂
// 创建默认过滤器
ObjectInputFilter defaultFilter = ObjectInputFilter.Config.createFilter(
"java.base.**;!*" // 默认过滤器
);
// 设置默认过滤器
ObjectInputFilter.Config.setSerialFilter(defaultFilter); // 设置默认过滤器
}
}
过滤器工厂让你能根据上下文动态创建过滤器。
实际应用:网络反序列化
网络反序列化需要严格的安全策略:
import java.io.*;
import java.net.*;
import java.util.function.*;
// 网络反序列化示例
public class NetworkDeserialization {
public static void main(String[] args) {
// 创建严格的网络过滤器
ObjectInputFilter networkFilter = ObjectInputFilter.Config.createFilter(
"java.base.**;!java.lang.Runtime;!java.lang.ProcessBuilder;!*" // 严格过滤器
);
try (ServerSocket serverSocket = new ServerSocket(8080)) { // 创建服务器套接字
Socket socket = serverSocket.accept(); // 接受连接
try (ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) {
// 设置网络过滤器
ois.setObjectInputFilter(networkFilter); // 设置过滤器
// 反序列化对象
Object obj = ois.readObject(); // 读取对象
System.out.println("反序列化对象: " + obj); // 输出对象
} catch (ClassNotFoundException e) {
e.printStackTrace(); // 处理异常
}
} catch (IOException e) {
e.printStackTrace(); // 处理异常
}
}
}
网络反序列化用严格的过滤器,防止恶意代码执行。
实际应用:文件反序列化
文件反序列化可以使用相对宽松的策略:
import java.io.*;
import java.nio.file.*;
import java.util.function.*;
// 文件反序列化示例
public class FileDeserialization {
public static void main(String[] args) {
// 创建文件过滤器(相对宽松)
ObjectInputFilter fileFilter = ObjectInputFilter.Config.createFilter(
"java.**;!java.lang.Runtime;!java.lang.ProcessBuilder" // 文件过滤器
);
Path filePath = Paths.get("data.ser"); // 文件路径
try (ObjectInputStream ois = new ObjectInputStream(
Files.newInputStream(filePath))) { // 创建输入流
// 设置文件过滤器
ois.setObjectInputFilter(fileFilter); // 设置过滤器
// 反序列化对象
Object obj = ois.readObject(); // 读取对象
System.out.println("反序列化对象: " + obj); // 输出对象
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace(); // 处理异常
}
}
}
文件反序列化用相对宽松的过滤器,但也要拒绝危险类。
实际应用:RMI 反序列化
RMI 反序列化需要特殊的安全策略:
import java.io.*;
import java.rmi.*;
import java.util.function.*;
// RMI 反序列化示例
public class RMIDeserialization {
public static void main(String[] args) {
// 创建 RMI 过滤器
ObjectInputFilter rmiFilter = ObjectInputFilter.Config.createFilter(
"java.base.**;java.rmi.**;!java.lang.Runtime;!java.lang.ProcessBuilder;!*" // RMI 过滤器
);
// 设置 RMI 过滤器工厂
BinaryOperator<ObjectInputFilter> rmiFilterFactory = (current, next) -> {
// RMI 特定的过滤器逻辑
if (next != null) {
return ObjectInputFilter.merge(rmiFilter, next); // 合并过滤器
}
return rmiFilter; // 返回 RMI 过滤器
};
// 设置过滤器工厂
ObjectInputFilter.Config.setSerialFilterFactory(rmiFilterFactory); // 设置工厂
// RMI 反序列化会自动使用 RMI 过滤器
}
}
RMI 反序列化用专门的过滤器,确保安全性。
过滤器模式
过滤器模式语法:
import java.io.*;
// 过滤器模式示例
public class FilterPatternExample {
public static void main(String[] args) {
// 模式语法:
// - java.base.** : 允许 java.base 包及其子包
// - !java.lang.Runtime : 拒绝 java.lang.Runtime 类
// - * : 允许所有类
// - !* : 拒绝所有类
// 示例 1:只允许 java.base 包
ObjectInputFilter filter1 = ObjectInputFilter.Config.createFilter(
"java.base.**;!*" // 只允许 java.base 包
);
// 示例 2:允许 java 包,但拒绝危险类
ObjectInputFilter filter2 = ObjectInputFilter.Config.createFilter(
"java.**;!java.lang.Runtime;!java.lang.ProcessBuilder;!*" // 允许 java 包,拒绝危险类
);
// 示例 3:允许特定类
ObjectInputFilter filter3 = ObjectInputFilter.Config.createFilter(
"com.example.**;!*" // 只允许 com.example 包
);
// 示例 4:拒绝所有类(最严格)
ObjectInputFilter filter4 = ObjectInputFilter.Config.createFilter(
"!*" // 拒绝所有类
);
}
}
过滤器模式语法灵活,能满足各种安全需求。
自定义过滤器
可以创建自定义过滤器:
import java.io.*;
import java.util.function.*;
// 自定义过滤器示例
public class CustomFilterExample {
public static void main(String[] args) {
// 创建自定义过滤器
ObjectInputFilter customFilter = new ObjectInputFilter() {
@Override
public Status checkInput(FilterInfo filterInfo) {
// 获取类信息
Class<?> clazz = filterInfo.serialClass(); // 获取序列化类
long arrayLength = filterInfo.arrayLength(); // 获取数组长度
long depth = filterInfo.depth(); // 获取深度
long references = filterInfo.references(); // 获取引用数
long streamBytes = filterInfo.streamBytes(); // 获取流字节数
// 自定义过滤逻辑
if (clazz != null) {
String className = clazz.getName(); // 获取类名
// 拒绝危险类
if (className.equals("java.lang.Runtime") ||
className.equals("java.lang.ProcessBuilder")) {
return Status.REJECTED; // 拒绝
}
// 允许特定包
if (className.startsWith("java.base.")) {
return Status.ALLOWED; // 允许
}
}
// 检查数组长度
if (arrayLength > 1000000) { // 数组太大
return Status.REJECTED; // 拒绝
}
// 检查深度
if (depth > 100) { // 深度太深
return Status.REJECTED; // 拒绝
}
// 检查流大小
if (streamBytes > 10000000) { // 流太大
return Status.REJECTED; // 拒绝
}
return Status.UNDECIDED; // 未决定
}
};
// 使用自定义过滤器
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(new byte[0]))) {
ois.setObjectInputFilter(customFilter); // 设置自定义过滤器
// 反序列化操作
} catch (IOException e) {
e.printStackTrace(); // 处理异常
}
}
}
自定义过滤器能实现更复杂的过滤逻辑。
过滤器合并
可以合并多个过滤器:
import java.io.*;
// 过滤器合并示例
public class FilterMergeExample {
public static void main(String[] args) {
// 创建多个过滤器
ObjectInputFilter filter1 = ObjectInputFilter.Config.createFilter(
"java.base.**;!*" // 过滤器 1
);
ObjectInputFilter filter2 = ObjectInputFilter.Config.createFilter(
"java.util.**;!*" // 过滤器 2
);
// 合并过滤器
ObjectInputFilter mergedFilter = ObjectInputFilter.merge(filter1, filter2); // 合并
// 使用合并后的过滤器
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(new byte[0]))) {
ois.setObjectInputFilter(mergedFilter); // 设置合并后的过滤器
// 反序列化操作
} catch (IOException e) {
e.printStackTrace(); // 处理异常
}
}
}
合并过滤器能组合多个安全策略。
注意事项
注意事项一:性能影响
过滤器可能影响性能:
// 性能影响
// 过滤器会在每次反序列化时调用
// 复杂的过滤逻辑可能影响性能
// 建议使用简单的模式匹配
过滤器可能影响性能,建议使用简单的模式匹配。
注意事项二:配置顺序
过滤器的配置顺序很重要:
// 配置顺序
// 1. 先设置过滤器工厂
// 2. 再设置默认过滤器
// 3. 最后设置上下文特定的过滤器
配置顺序很重要,要按照正确的顺序设置。
注意事项三:测试
充分测试过滤器配置:
// 测试过滤器
// 1. 测试允许的类
// 2. 测试拒绝的类
// 3. 测试边界情况
充分测试确保过滤器配置正确。
最佳实践
最佳实践一:使用严格模式
网络反序列化使用严格模式:
// 网络反序列化使用严格模式
ObjectInputFilter networkFilter = ObjectInputFilter.Config.createFilter(
"java.base.**;!*" // 只允许 java.base 包
);
网络反序列化使用严格模式,防止攻击。
最佳实践二:拒绝危险类
始终拒绝危险类:
// 拒绝危险类
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"java.**;!java.lang.Runtime;!java.lang.ProcessBuilder;!*" // 拒绝危险类
);
始终拒绝危险类,防止代码执行。
最佳实践三:记录日志
记录过滤器的决策:
// 记录日志
ObjectInputFilter filter = filterInfo -> {
Status status = // 过滤逻辑
if (status == Status.REJECTED) {
logger.warning("拒绝反序列化: " + filterInfo.serialClass()); // 记录日志
}
return status; // 返回状态
};
记录日志帮助调试和审计。
总结
上下文特定的反序列化过滤器是 JDK 17 的一个重要安全特性,让你能为不同的反序列化上下文设置不同的过滤器,提升了反序列化安全性。这玩意儿虽然配置可能稍微复杂一点,但安全性提升明显,值得投入。
建议在实际项目中为不同的反序列化上下文设置不同的过滤器,特别是网络反序列化,要使用严格模式。虽然配置可能需要一些工作,但能让应用更安全。下一篇文章咱就聊聊上下文特定的反序列化过滤器实战,看看怎么防止反序列化攻击。兄弟们有啥问题随时问,鹏磊会尽量解答。