You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确关闭释放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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 00:12:34