【问题标题】:Java Serialization MethodsJava 序列化方法
【发布时间】:2014-10-01 18:54:36
【问题描述】:

正如securecoding site 解释的那样:

块引用 这个不兼容的代码示例显示了一个带有私有构造函数的类 Ser,表明该类外部的代码应该无法创建它的实例。该类实现 java.io.Serializable 并定义公共 readObject() 和 writeObject() 方法。因此,不受信任的代码可以通过 readObject() 获取重构的对象,并可以通过 writeObject() 写入流。

public class Ser implements Serializable {
  private final long serialVersionUID = 123456789;
  private Ser() {
    // initialize
  }
  public static void writeObject(final ObjectOutputStream stream)
    throws IOException {
    stream.defaultWriteObject();
  }
  public static void readObject(final ObjectInputStream stream)
      throws IOException, ClassNotFoundException {
    stream.defaultReadObject();
  }
}

如您所知,writeObject 和 readObject 方法应该被定义为私有(并且也没有 static 关键字!)并且这些方法不被 JVM 调用。

我的问题是:为什么这些方法不安全。这些方法甚至不被 JVM 调用!!我想要一个示例代码,向我展示此代码可能不安全,攻击者可以访问我们的数据。

我们将不胜感激。

【问题讨论】:

  • JVM 为什么不调用它们?是什么阻止 EvilClass 调用 Ser.readObject(myInputStream)
  • @Powerlord 看看我的回答。

标签: java serialization serializable secure-coding


【解决方案1】:

这里显示的代码肯定有一个错误,但这并不是真正的安全漏洞,它只是一个编程错误。不幸的是,安全编码网站在一些关于如何修复它的声明中是不正确的。请参阅该文章下方的 cmets;他们讨论了一些错误。

首先,正如您所指出的,由于这些方法被声明为static,它们根本不会被序列化机制调用。 (序列化主要是基于库的,而不是基于 JVM 的。)错误是,如果您想使用这些方法自定义序列化格式,它根本行不通。无论您在这些方法中添加什么,都将使用默认的序列化格式。

(这也有点奇怪,自定义的 readObjectwriteObject 方法只调用默认的读/写例程,根本不提供任何自定义,但这可能是为了举例。)

这段代码不安全吗?不,不是。它只是不会按预期的方式工作。

用户 Powerlord 询问攻击者是否不能只调用 Ser.readObject(myInputStream)。那是行不通的,因为 defaultReadObject 方法只允许在对象的反序列化期间被调用。如果不是,它会抛出NotActiveException

此代码的修复方法是更改​​方法,使其成为private 实例方法而不是public static 方法。这将导致序列化机制调用这些方法,以便它们可以实现一些格式自定义。

但是,引用的文章不正确指出这将阻止攻击者创建不需要的实例。反序列化会绕过私有构造函数是正确的。但是将readObjectwriteObject 设为私有将不会阻止攻击者反序列化尽可能多的此类实例。这大概就是霸王所关心的吧。我可以轻松地编造我自己的字节流并从中反序列化对象,想多少次都可以,我可以选择任何内容:

ObjectInputStream ois = new ObjectInputStream(myInputStream);
Ser anotherSerInstance = (Ser)ois.readObject();

为了防止这种情况,Ser 类必须在readObject 中实现一些检查,如果它想防止反序列化,则抛出类似InvalidObjectException 的东西。另一种方法是实现 readResolve 并返回一个已经创建的实例(例如单例)而不是创建一个新实例。

有关进一步的讨论,请参阅 Bloch,Effective Java,第 74-78 条。请注意,如果您想要单例,它建议使用enum 而不是readResolve

【讨论】:

    【解决方案2】:

    网站不正确。 Object Serialization Specification 明确指出writeObject() 的签名是

    private void writeObject(ObjectOutputStream stream)
        throws IOException;
    

    同样signature of readObject()

    private void readObject(ObjectInputStream stream)
        throws IOException, ClassNotFoundException;
    

    如果您将它们设为public static,它们将不会被调用。他们都在这里。

    【讨论】:

      猜你喜欢
      • 2013-09-04
      • 2012-11-07
      • 1970-01-01
      • 1970-01-01
      • 2023-04-07
      • 2010-12-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多