专业的JAVA编程教程与资源

网站首页 > java教程 正文

安全技术研究-反序列化漏洞

temp10 2024-12-01 04:18:15 java教程 13 ℃ 0 评论


介绍

安全技术研究-反序列化漏洞

不安全反序列化是一种漏洞,通过畸形的和不受信任的数据输入导致应用程序不安全地反序列化。它被用来劫持应用程序端的逻辑流,可能导致任意代码的执行。尽管这并不是一种简单的攻击,但由于对会服务器造成的严重影响,它在OWASP TOP10最新迭代中被列为软件和数据完整性失败风险的一部分。

定义:将对象状态或数据结构转换为可存储或可传输格式的过程称为序列化。反序列化与之相反——提取序列化数据以重建原始对象版本的过程。

当攻击者将特别定制的恶意数据传递到用户提供的要反序列化的数据中时,就会出现不安全的反序列化问题。这可能导致向应用程序中注入任意对象,从而影响服务器执行恶意操作。

影响及危害

成功的不安全反序列化攻击可能导致目标系统的机密性、完整性和可用性完全受损,经常被提及的Equifax漏洞可能是目前发生的最严重的由于反序列化漏洞导致的安全事件。在Equifax的案例中,利用Struts 2框架的不安全Java反序列化攻击导致了远程代码执行,进而导致了历史上最大的数据泄露。

如何防护

我们要从体系结构的角度考虑开发项目,以确定何时何地需要序列化是很重要的。如果没有必要,可以考虑在传递数据时使用更简单的格式,比如json。

在必须使用反序列化技术同时又不破坏应用程序的操作完整性的情况下,开发人员可以实现一系列深入防御的措施,以减少被利用的机会。

  • 使用只允许基本数据类型的序列化。
  • 使用提供加密签名和加密特性的序列化库,以确保获得的序列化数据不被篡改。
  • 在反序列化之前进行身份验证。
  • 在隔离环境中使用低特权账号运行反序列化的代码。

最后,如果可能的话,用纯数据序列化格式(如JSON)替换对象序列化。

代码实现-JAVA:

Java为通过ObjectInputStream和ObjectOutputStream工具实现Serializable接口对象的本地序列化。二进制格式直接通过名称引用最终动态加载的类,前提是它们位于类路径中。这将允许实例化开发人员最初并不打算使用的类的对象,因此非常重要的一点就是尽量不要去反序列哪些不可信的数据。

开发人员可以通过提供writeReplace和readResolve等回调来定制序列化过程的一些配置。这个功能可能会被攻击者通过构建复杂的对象来构建攻击链来利用,最终导致在目标上执行代码或其他操作。特别是在使用复杂而知名的库和框架时,攻击者可能会利用ysoserial等公开可用的工具来轻松构建适当的有效恶意负载。

下面的Spring控制器使用来自客户端请求的数据来反序列化对象:

@Controller
public class MyController {
    @RequestMapping(value = "/", method = GET)
    public String index(@CookieValue(value = "myCookie") String myCookieString) {
        // decode the Base64 cookie value
        byte[] myCookieBytes = Base64.getDecoder().decode(myCookieString);

        // use those bytes to deserialize an object
        ByteArrayInputStream buffer = new ByteArrayInputStream(myCookieBytes);
        try (ObjectInputStream stream = new ObjectInputStream(buffer)) {
            MyObject myObject = stream.readObject();

            // ...
        }
    }
}

代码防护

永远不要将用户提供的输入直接传递给Java反序列化机制。可能的话选择只有数据的序列化格式,如JSON。

如果确实需要对不可信数据进行反序列化,可以考虑采用允许列表方法,只允许对某些类的对象进行反序列化。

从Java版本9开始,可以通过几种方式指定反序列化筛选器。例如在使用ObjectInputStream对象之前使用setObjectInputFilter方法。setObjectInputFilter方法采用实现过滤逻辑的方法作为参数。下面的例子是过滤器只允许反序列化MyClass类:

ObjectInputStream objectInputStream = new ObjectInputStream(buffer);
stream.setObjectInputFilter(MyFilter::myFilter);


public class MyFilter {
    static ObjectInputFilter.Status myFilter(ObjectInputFilter.FilterInfo info) {
        Class<?> serialClass = info.serialClass();
        if (serialClass != null) {
            return serialClass.getName().equals(MyClass.class.getName())
                    ? ObjectInputFilter.Status.ALLOWED
                    : ObjectInputFilter.Status.REJECTED;
        }
        return ObjectInputFilter.Status.UNDECIDED;
    }
}

另外,也可以通过专门化ObjectInputStream对象来实现类似的解决方案。下面的代码片段只允许反序列化MyClass类的实例:

public class MyFilteringInputStream extends ObjectInputStream {
    public MyFilteringInputStream(InputStream inputStream) throws IOException {
        super(inputStream);
    }

    @Override
    protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
        if (!objectStreamClass.getName().equals(MyClass.class.getName())) {
            throw new InvalidClassException("Forbidden class", objectStreamClass.getName());
        }
        return super.resolveClass(objectStreamClass);
    }
}

然后就可以以通常的方式调用反序列化:

ObjectInputStream objectInputStream = new MyFilteringInputStream(buffer);
objectInputStream.readObject();

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表