dubbo反序列化问题-Hessian2安全加固和修复
字数 1569 2025-08-25 22:59:03

Dubbo Hessian2反序列化安全加固指南

0x01 背景与问题概述

Dubbo作为一款流行的分布式服务框架,默认使用Hessian2作为序列化协议。然而,Hessian2反序列化存在严重的安全隐患:

  1. Dubbo默认的Hessian2反序列化缺乏安全保护机制
  2. 攻击者可以构造恶意序列化数据触发远程代码执行
  3. 常见的反序列化gadget链(如Rome、XBean等)可以被利用

0x02 加固方案比较

针对Dubbo反序列化问题,主要有三种加固方案:

  1. 修改反序列化类型(不推荐)

    • 改为原生Java、Fastjson等仍存在安全问题
    • 业务修改风险高
  2. RPC改为HTTP API(不推荐)

    • 业务开发量大
    • 架构改动成本高
  3. 加固Hessian2(推荐)

    • 通过SPI机制扩展Hessian2
    • 添加反序列化黑名单
    • 对现有业务影响小

0x03 SPI机制详解

Java SPI机制

SPI(Service Provider Interface)是Java提供的服务发现机制,核心组件:

  1. 接口定义(如Fruit
  2. 实现类(如AppleMango
  3. META-INF/services/接口全限定名文件
    • 内容为实现类的全限定名

加载方式:

ServiceLoader<Fruit> fruits = ServiceLoader.load(Fruit.class);
fruits.forEach(fruit -> System.out.println(fruit.name()));

Spring SPI机制

与Java SPI类似,但配置文件为META-INF/spring.factories,格式:

接口全限定名=实现类全限定名

加载方式:

List<Fruit> fruits = SpringFactoriesLoader.loadFactories(Fruit.class, null);

Dubbo SPI机制

Dubbo自定义了更强大的SPI机制:

  1. 配置文件位置:

    • META-INF/dubbo/
    • META-INF/dubbo/internal/
    • META-INF/services/
  2. 兼容处理:

    • 自动处理org.apachecom.alibaba包名转换
  3. 核心类:

    • ExtensionLoader负责加载扩展实现

0x04 Hessian2安全加固实现

实现方案

通过Dubbo SPI机制扩展Hessian2,添加反序列化黑名单:

  1. 创建自定义序列化工厂MyHessian2Serialization
  2. 实现自定义输入处理MyHessian2ObjectInput
  3. 扩展Hessian2Input添加黑名单检查

具体实现代码

1. MyHessian2Serialization

package com.threedr3am.learn.server.boot.serialize;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.serialize.ObjectInput;
import org.apache.dubbo.common.serialize.ObjectOutput;
import org.apache.dubbo.common.serialize.Serialization;
import org.apache.dubbo.common.serialize.hessian2.Hessian2ObjectOutput;

public class MyHessian2Serialization implements Serialization {
    @Override
    public byte getContentTypeId() { return 22; }
    
    @Override
    public String getContentType() { return "x-application/hessian2"; }
    
    @Override
    public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
        return new Hessian2ObjectOutput(out);
    }
    
    @Override
    public ObjectInput deserialize(URL url, InputStream is) throws IOException {
        return new MyHessian2ObjectInput(is);
    }
}

2. MyHessian2ObjectInput

package com.threedr3am.learn.server.boot.serialize;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import org.apache.dubbo.common.serialize.ObjectInput;
import org.apache.dubbo.common.serialize.hessian2.Hessian2SerializerFactory;

public class MyHessian2ObjectInput implements ObjectInput {
    private final MyHessian2Input mH2i;
    
    public MyHessian2ObjectInput(InputStream is) {
        mH2i = new MyHessian2Input(is);
        mH2i.setSerializerFactory(Hessian2SerializerFactory.SERIALIZER_FACTORY);
    }
    
    // 各种read方法实现...
    @Override
    public Object readObject() throws IOException {
        return mH2i.readObject();
    }
}

3. MyHessian2Input(核心安全检查)

package com.threedr3am.learn.server.boot.serialize;

import com.alibaba.com.caucho.hessian.io.Hessian2Input;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class MyHessian2Input extends Hessian2Input {
    private static final Set<String> blackList = new HashSet<>();
    
    static {
        // 初始化黑名单
        blackList.add("org.apache.xbean.naming.context.ContextUtil.ReadOnlyBinding");
        blackList.add("org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor");
        blackList.add("com.rometools.rome.feed.impl.EqualsBean");
        blackList.add("com.caucho.naming.QName");
        // 添加更多已知gadget类...
    }
    
    public MyHessian2Input(InputStream is) { super(is); }
    
    // 重写所有readObject方法添加安全检查
    @Override
    public Object readObject() throws IOException {
        checkClassDef();
        return super.readObject();
    }
    
    // 类定义检查
    void checkClassDef() {
        if (_classDefs == null || _classDefs.isEmpty()) return;
        
        for (Object c : _classDefs) {
            Field[] fields = c.getClass().getDeclaredFields();
            if (fields.length == 2) {
                fields[0].setAccessible(true);
                try {
                    String type = (String) fields[0].get(c);
                    if (blackList.contains(type)) 
                        _classDefs = new ArrayList(); // 清空恶意类定义
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

SPI配置文件

resources/META-INF/dubbo/下创建org.apache.dubbo.common.serialize.Serialization文件,内容:

MyHessian2=com.threedr3am.learn.server.boot.serialize.MyHessian2Serialization

服务端配置

application.properties中指定序列化方式:

dubbo.provider.serialization=MyHessian2

0x05 关键注意事项

  1. 双向部署:服务提供者和消费者两端都需要部署上述修改
  2. 黑名单维护:及时更新MyHessian2Input中的黑名单
  3. 已知gadget类(部分):
    • org.apache.xbean.naming.context.ContextUtil.ReadOnlyBinding
    • org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor
    • com.rometools.rome.feed.impl.EqualsBean
    • com.caucho.naming.QName

0x06 扩展建议

  1. 持续监控:关注新的反序列化gadget发现,及时更新黑名单
  2. 白名单机制:对于高安全要求场景,可考虑实现白名单机制
  3. 版本升级:关注Dubbo官方安全更新,及时升级版本

通过以上方案,可以在不改变Dubbo核心架构的情况下,有效防御Hessian2反序列化攻击,保障分布式系统安全。

Dubbo Hessian2反序列化安全加固指南 0x01 背景与问题概述 Dubbo作为一款流行的分布式服务框架,默认使用Hessian2作为序列化协议。然而,Hessian2反序列化存在严重的安全隐患: Dubbo默认的Hessian2反序列化缺乏安全保护机制 攻击者可以构造恶意序列化数据触发远程代码执行 常见的反序列化gadget链(如Rome、XBean等)可以被利用 0x02 加固方案比较 针对Dubbo反序列化问题,主要有三种加固方案: 修改反序列化类型 (不推荐) 改为原生Java、Fastjson等仍存在安全问题 业务修改风险高 RPC改为HTTP API (不推荐) 业务开发量大 架构改动成本高 加固Hessian2 (推荐) 通过SPI机制扩展Hessian2 添加反序列化黑名单 对现有业务影响小 0x03 SPI机制详解 Java SPI机制 SPI(Service Provider Interface)是Java提供的服务发现机制,核心组件: 接口定义(如 Fruit ) 实现类(如 Apple 、 Mango ) META-INF/services/接口全限定名 文件 内容为实现类的全限定名 加载方式: Spring SPI机制 与Java SPI类似,但配置文件为 META-INF/spring.factories ,格式: 加载方式: Dubbo SPI机制 Dubbo自定义了更强大的SPI机制: 配置文件位置: META-INF/dubbo/ META-INF/dubbo/internal/ META-INF/services/ 兼容处理: 自动处理 org.apache 和 com.alibaba 包名转换 核心类: ExtensionLoader 负责加载扩展实现 0x04 Hessian2安全加固实现 实现方案 通过Dubbo SPI机制扩展Hessian2,添加反序列化黑名单: 创建自定义序列化工厂 MyHessian2Serialization 实现自定义输入处理 MyHessian2ObjectInput 扩展 Hessian2Input 添加黑名单检查 具体实现代码 1. MyHessian2Serialization 2. MyHessian2ObjectInput 3. MyHessian2Input(核心安全检查) SPI配置文件 在 resources/META-INF/dubbo/ 下创建 org.apache.dubbo.common.serialize.Serialization 文件,内容: 服务端配置 在 application.properties 中指定序列化方式: 0x05 关键注意事项 双向部署 :服务提供者和消费者两端都需要部署上述修改 黑名单维护 :及时更新 MyHessian2Input 中的黑名单 已知gadget类 (部分): org.apache.xbean.naming.context.ContextUtil.ReadOnlyBinding org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor com.rometools.rome.feed.impl.EqualsBean com.caucho.naming.QName 0x06 扩展建议 持续监控 :关注新的反序列化gadget发现,及时更新黑名单 白名单机制 :对于高安全要求场景,可考虑实现白名单机制 版本升级 :关注Dubbo官方安全更新,及时升级版本 通过以上方案,可以在不改变Dubbo核心架构的情况下,有效防御Hessian2反序列化攻击,保障分布式系统安全。