如何正确关闭释放ObjectInputStream并安全使用readObject方法?
正确使用ObjectInputStream的解决方案
一、解决资源未正确关闭的问题
原代码未确保ObjectInputStream在所有场景下被关闭——一旦readObject抛出异常,流资源会被遗漏。Java 7及以上的try-with-resources语句可自动关闭实现AutoCloseable接口的资源(ObjectInputStream和ByteArrayInputStream均符合),无需手动在finally块中处理,能覆盖正常执行、异常抛出等所有场景。
资源管理的正确写法:
try (var byteArrayIn = new ByteArrayInputStream(Base64.getDecoder().decode(encoded)); var objectIn = new ObjectInputStream(byteArrayIn)) { // 读取对象逻辑 } catch (...) { // 异常处理 }
二、解决readObject的安全风险问题
针对静态分析指出的「输入未验证」「存储型日志伪造」风险,需从以下维度处理:
1. 严格校验反序列化结果类型
不能直接强制转换为Map<?,?>,需进一步校验Map的键值类型是否符合业务预期,避免恶意对象注入:
Object obj = objectIn.readObject(); if (!(obj instanceof Map)) { throw new IllegalArgumentException("反序列化结果非法,不是Map类型"); } Map<?,?> rawMap = (Map<?,?>) obj; // 校验键值类型(示例为String类型,可根据实际业务调整) for (Map.Entry<?,?> entry : rawMap.entrySet()) { if (!(entry.getKey() instanceof String) || !(entry.getValue() instanceof String)) { throw new IllegalArgumentException("Map的键或值类型不符合要求"); } }
2. 限制允许反序列化的类
反序列化本身存在安全风险,恶意构造的序列化数据可能执行恶意代码。可通过重写ObjectInputStream.resolveClass方法,仅允许白名单内的类被反序列化:
var objectIn = new ObjectInputStream(byteArrayIn) { @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { String className = desc.getName(); // 白名单示例:仅允许HashMap、LinkedHashMap和String类 if (className.equals("java.util.HashMap") || className.equals("java.util.LinkedHashMap") || className.equals("java.lang.String")) { return super.resolveClass(desc); } throw new InvalidClassException("禁止反序列化的类", className); } };
3. 清理日志敏感内容
如果Map中的内容会被写入审计日志,需过滤掉可能篡改日志结构的特殊字符(如换行符\n、回车符\r),防止存储型日志伪造:
// 清理Map中的危险字符 Map<String,String> cleanedMap = new HashMap<>(); for (Map.Entry<String,String> entry : safeMap.entrySet()) { String cleanedKey = entry.getKey().replaceAll("[\\n\\r]", ""); String cleanedValue = entry.getValue().replaceAll("[\\n\\r]", ""); cleanedMap.put(cleanedKey, cleanedValue); }
完整修改后的代码
import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InvalidClassException; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; import java.util.HashMap; import java.util.Map; import java.util.Base64; public Map<String, String> getMap(String encoded) { try (var byteArrayIn = new ByteArrayInputStream(Base64.getDecoder().decode(encoded)); var objectIn = new ObjectInputStream(byteArrayIn) { @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { String className = desc.getName(); // 白名单允许的类 if (className.equals("java.util.HashMap") || className.equals("java.util.LinkedHashMap") || className.equals("java.lang.String")) { return super.resolveClass(desc); } throw new InvalidClassException("禁止反序列化的类", className); } }) { Object obj = objectIn.readObject(); if (!(obj instanceof Map)) { throw new IllegalArgumentException("反序列化结果非法,不是Map类型"); } Map<?, ?> rawMap = (Map<?, ?>) obj; // 校验键值类型 for (Map.Entry<?, ?> entry : rawMap.entrySet()) { if (!(entry.getKey() instanceof String) || !(entry.getValue() instanceof String)) { throw new IllegalArgumentException("Map的键或值类型不符合要求"); } } @SuppressWarnings("unchecked") Map<String, String> safeMap = (Map<String, String>) rawMap; // 清理日志危险字符 Map<String, String> cleanedMap = new HashMap<>(); for (Map.Entry<String, String> entry : safeMap.entrySet()) { String cleanedKey = entry.getKey().replaceAll("[\\n\\r]", ""); String cleanedValue = entry.getValue().replaceAll("[\\n\\r]", ""); cleanedMap.put(cleanedKey, cleanedValue); } return cleanedMap; } catch (IOException | ClassNotFoundException | IllegalArgumentException e) { // 异常处理:避免将原始异常信息直接写入审计日志,防止泄露敏感内容 return Map.of(); } }
内容的提问来源于stack exchange,提问作者PatPanda
相关产品推荐
相关产品推荐

