Java零基础-反序列化和序列化

举报
喵手 发表于 2024/11/29 01:01:05 2024/11/29
【摘要】 哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这...

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前言

Java是一种广泛使用的编程语言,它具有跨平台,高性能,安全等优点,因此受到了广泛关注。然而,Java语言并非完美无缺,仍然存在一些安全漏洞。本文将介绍Java中的一个安全漏洞——反序列化漏洞,以及如何在代码开发中避免出现此类漏洞。

摘要

本文将详细介绍Java中的反序列化漏洞,包括反序列化的概念、攻击方式、应用场景和防范措施等内容。同时,本文还将介绍反序列化漏洞的相关代码和应用案例,并分析其优缺点。最后,本文将通过编写测试用例来验证反序列化漏洞的存在,同时总结全文内容。

简介

反序列化是指将二进制数据转换为对象的过程。在Java语言中,反序列化通常通过ObjectInputStream类来实现。反序列化在很多情况下非常有用,比如在网络传输中传递对象,或者在分布式系统中将对象序列化到磁盘上以进行持久化。但是,如果反序列化不加控制地进行,就可能引发一些安全漏洞,例如攻击者可以通过构造特定的序列化数据,来控制反序列化过程并执行恶意代码。

源代码解析

在 java.io.ObjectInputStream 类的 readObject 方法中,反序列化对象的过程主要分为以下步骤:

  1. 读取序列化数据的头部信息,包括版本号、类名等。

  2. 通过类名和版本号信息加载相应的类定义,这个过程被称为 class resolution (类解析)。

  3. 检查反序列化的类是否有效,如果出现异常则抛出 InvalidClassException 异常。

  4. 根据类定义信息逐一反序列化类的成员变量。

代码示例:

public final Object readObject() throws ClassNotFoundException, IOException {
    ...
    byte tc = bin.peekByte();
    switch (tc) {
        case TC_NULL:
            ...
        case TC_REFERENCE:
            ...
        case TC_CLASS:
            ...
        case TC_OBJECT:
            ...
        case TC_ARRAY:
            ...
        case TC_STRING:
            ...
        case TC_LONGSTRING:
            ...
        case TC_ENUM:
            ...
        case TC_PROXYCLASSDESC:
            ...
        case TC_EXCEPTION:
            ...
        default:
            throw new StreamCorruptedException(
                    String.format("invalid type code: %02X", tc));
    }
}

应用场景案例

反序列化漏洞在很多Java应用程序中都存在,特别是那些涉及到网络传输和数据持久化的应用程序。下面是一个具体的应用案例:

public class Server {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ServerSocket serverSocket = new ServerSocket(8888);
        while (true) {
            Socket socket = serverSocket.accept();
            ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
            Object obj = ois.readObject();
            System.out.println("Received object: " + obj);
        }
    }
}

上述代码是一个简单的服务端程序,它从客户端通过ObjectInputStream读取序列化数据,然后将其输出到控制台。攻击者可以构造特定的序列化数据,来执行恶意代码并攻击服务端,例如:

public class MaliciousObject implements Serializable {
    private void readObject(ObjectInputStream ois) throws Exception {
        Runtime.getRuntime().exec("calc");
    }
}

public class Client {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Socket socket = new Socket("localhost", 8888);
        ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
        oos.writeObject(new MaliciousObject());
        oos.flush();
    }
}

上述代码中,MaliciousObject 类重载了 readObject 方法,在反序列化的过程中执行了恶意代码 Runtime.getRuntime().exec(“calc”) ,以此攻击服务端。如果服务端没有对反序列化进行特殊处理,就会执行这段恶意代码并打开计算器。

优缺点分析

反序列化漏洞的优势在于攻击者可以通过构造恶意序列化数据,以较低的成本来攻击目标系统。此外,反序列化漏洞通常比较难检测,因为攻击者可以伪装成正常的序列化过程,而不被发现。

反序列化漏洞的缺点在于,它需要攻击者具备一定的Java编程和网络传输知识,攻击成本虽然较低,但攻击门槛较高。同时,由于Java开发人员可以通过特殊的处理方式来避免反序列化漏洞,因此漏洞的影响范围有限。

类代码方法介绍

在Java中,为了避免反序列化漏洞,开发人员需要注意以下几点:

  1. 只反序列化信任的数据,不要反序列化不可信的数据,比如来自网络或者未知来源的数据。

  2. 对于可序列化的对象,尽可能使用 serialVersionUID 显示声明版本号,避免在序列化过程中由于版本号不同导致反序列化失败。

  3. 反序列化过程中对反序列化类进行严格校验,避免恶意类的注入。

  4. 对于反序列化后的对象,不要直接使用其中的方法和属性,而是需要进行必要的验证和过滤。

代码示例:

public class SecureObjectInputStream extends ObjectInputStream {
    public SecureObjectInputStream(InputStream in) throws IOException {
        super(in);
    }

    @Override
    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
        if (!"com.example.TrustedClass".equals(desc.getName())) {
            throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
        }
        return super.resolveClass(desc);
    }

    @Override
    protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
        ObjectStreamClass desc = super.readClassDescriptor();
        if (!"com.example.TrustedClass".equals(desc.getName())) {
            throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
        }
        return desc;
    }
}

上述代码是一个自定义的安全反序列化类 SecureObjectInputStream ,它通过重载 resolveClass 和 readClassDescriptor 方法,对反序列化过程进行了严格的校验和限制,避免了恶意类的注入。

测试用例

为了验证反序列化漏洞的存在,我们可以编写以下测试用例:

public class SerializationTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(new MaliciousObject());
        oos.flush();

        SecureObjectInputStream sois = new SecureObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        Object obj = sois.readObject();
        System.out.println("Received object: " + obj);
    }
}

上述代码构造了一个 MaliciousObject 类的实例,并将其通过 ObjectOutputStream 序列化成字节数组。然后,我们使用我们自己定义的安全反序列化类 SecureObjectInputStream 对字节数组进行反序列化,如果反序列化成功并输出 “Received object: …” ,说明反序列化漏洞存在。

全文小结

本文主要介绍了Java中的一个安全漏洞——反序列化漏洞,包括反序列化的概念、攻击方式、应用场景和防范措施等内容。同时,本文还介绍了反序列化漏洞的相关代码。

… …

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

… …

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。

⭐️若有疑问,就请评论留言告诉我叭。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。