16、JDK 17 新特性:上下文特定的反序列化过滤器 JEP 415:增强反序列化安全性

反序列化攻击,鹏磊最烦的就是这个。以前用全局过滤器,所有反序列化都用同一个规则,不够灵活。不同的上下文需要不同的安全策略,比如从网络接收的数据和从本地文件读取的数据,安全要求不一样。JDK 17 的 JEP 415 引入了上下文特定的反序列化过滤器,让你能为不同的反序列化上下文设置不同的过滤器,安全性提升了不少。

上下文特定的反序列化过滤器是 JEP 415 引入的特性,允许为不同的反序列化上下文设置不同的过滤器。这玩意儿提升了反序列化安全性,让你能根据不同的上下文(比如网络、文件、RMI)设置不同的安全策略,防止反序列化攻击。虽然配置可能稍微复杂一点,但安全性提升明显,值得投入。

什么是反序列化攻击

反序列化攻击是指攻击者通过构造恶意的序列化数据,在反序列化时执行恶意代码。常见的攻击方式包括:

  1. 代码执行:通过反序列化执行恶意代码
  2. 拒绝服务:通过构造大量数据导致内存溢出
  3. 信息泄露:通过反序列化获取敏感信息
// 反序列化攻击示例(危险,不要在生产环境使用)
// 攻击者可能构造恶意的序列化数据
// 在反序列化时执行恶意代码
// 导致系统被攻击

反序列化攻击是严重的安全威胁,需要有效防护。

为什么需要上下文特定的过滤器

以前使用全局过滤器,所有反序列化都用同一个规则,有几个问题:

  1. 不够灵活:不同上下文需要不同的安全策略
  2. 配置困难:全局配置难以满足所有场景
  3. 安全性差:一刀切的策略可能不够安全

上下文特定的过滤器解决了这些问题。

基础用法

上下文特定的过滤器基础用法:

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 的一个重要安全特性,让你能为不同的反序列化上下文设置不同的过滤器,提升了反序列化安全性。这玩意儿虽然配置可能稍微复杂一点,但安全性提升明显,值得投入。

建议在实际项目中为不同的反序列化上下文设置不同的过滤器,特别是网络反序列化,要使用严格模式。虽然配置可能需要一些工作,但能让应用更安全。下一篇文章咱就聊聊上下文特定的反序列化过滤器实战,看看怎么防止反序列化攻击。兄弟们有啥问题随时问,鹏磊会尽量解答。

本文章最后更新于 2025-11-28