MST
星途 面试题库

面试题:Java反序列化漏洞修复的深入实践

假设你正在维护一个使用Java序列化机制的大型应用,检测到存在反序列化漏洞。详细阐述你会如何结合白名单策略与类加载器限制,全面修复该漏洞,并避免对现有业务逻辑产生重大影响。
40.7万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试
  1. 白名单策略实施
    • 定义白名单类集合
      • 梳理应用中实际需要反序列化的类,创建一个明确的白名单。例如,如果应用主要涉及用户信息、订单信息的反序列化,将对应的User类、Order类等加入白名单。
      • 可以在配置文件(如propertiesxml文件)中定义白名单,格式如下:
whitelist.classes=com.example.User,com.example.Order
  • 在反序列化过程中检查
    • 自定义一个ObjectInputStream的子类,重写resolveClass方法。
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.util.Arrays;
import java.util.List;

public class SafeObjectInputStream extends ObjectInputStream {
    private final List<String> whitelist;

    public SafeObjectInputStream(InputStream in, List<String> whitelist) throws IOException {
        super(in);
        this.whitelist = whitelist;
    }

    @Override
    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
        String className = desc.getName();
        if (!whitelist.contains(className)) {
            throw new InvalidClassException("Unauthorized class: " + className);
        }
        return super.resolveClass(desc);
    }
}
 - 在反序列化的代码处,使用自定义的`SafeObjectInputStream`。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Arrays;
import java.util.List;

public class DeserializeExample {
    public static void main(String[] args) {
        List<String> whitelist = Arrays.asList("com.example.User", "com.example.Order");
        try (ObjectInputStream ois = new SafeObjectInputStream(new FileInputStream("serializedObject"), whitelist)) {
            Object obj = ois.readObject();
            // 处理反序列化后的对象
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
  1. 类加载器限制
    • 自定义类加载器
      • 创建一个自定义类加载器,继承自ClassLoader
public class RestrictedClassLoader extends ClassLoader {
    private final List<String> allowedPackages;

    public RestrictedClassLoader(List<String> allowedPackages, ClassLoader parent) {
        super(parent);
        this.allowedPackages = allowedPackages;
    }

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        if (!isAllowedPackage(name)) {
            throw new ClassNotFoundException("Class not allowed: " + name);
        }
        return super.loadClass(name, resolve);
    }

    private boolean isAllowedPackage(String className) {
        for (String allowedPackage : allowedPackages) {
            if (className.startsWith(allowedPackage)) {
                return true;
            }
        }
        return false;
    }
}
  • 使用自定义类加载器进行反序列化
    • 修改反序列化代码,使用自定义类加载器。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Arrays;
import java.util.List;

public class DeserializeExample {
    public static void main(String[] args) {
        List<String> allowedPackages = Arrays.asList("com.example.");
        ClassLoader restrictedClassLoader = new RestrictedClassLoader(allowedPackages, DeserializeExample.class.getClassLoader());
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("serializedObject")) {
            @Override
            protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
                return restrictedClassLoader.loadClass(desc.getName());
            }
        }) {
            Object obj = ois.readObject();
            // 处理反序列化后的对象
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
  1. 最小化对现有业务逻辑影响
    • 封装修改
      • 将白名单策略和类加载器限制的代码封装在独立的模块或工具类中。这样,对于现有的业务逻辑代码,只需要在反序列化调用处进行少量修改,引入新的类(如SafeObjectInputStream)和配置(白名单配置)即可。
    • 逐步过渡
      • 可以先在测试环境全面测试修复方案,确保不影响现有业务功能。然后在生产环境采用灰度发布的方式,逐步将修复后的代码上线,观察业务运行情况,及时处理可能出现的兼容性问题。