Методы сериализации Java

Как сайт securecoding объясняет:

Цитата В этом примере кода, не соответствующего требованиям, показан класс 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 должны быть определены как private (а также без статического ключевого слова!), и эти методы не должны вызываться JVM.

мой вопрос: почему эти методы небезопасны. эти методы даже не вызывают JVM! Мне нужен пример кода, который показывает, что этот код может быть небезопасным, и злоумышленник может получить доступ к нашим данным.

любая помощь будет оценена.

2 ответа

Решение

Приведенный здесь код, безусловно, содержит ошибку, но на самом деле это не уязвимость безопасности, это просто ошибка программирования. И, к сожалению, безопасный код сайта неверен в некоторых заявлениях о том, как это исправить. Смотрите комментарии ниже этой статьи; они обсуждают некоторые из ошибок.

Во-первых, как вы заметили, так как эти методы объявлены static они не будут вызваны механизмом сериализации. (Сериализация в основном основана на библиотеках, а не на JVM.) Ошибка в том, что если вы хотите настроить формат сериализации с помощью этих методов, он просто не будет работать. Формат сериализации по умолчанию будет использоваться независимо от того, что вы указали в этих методах.

(Это также несколько странно, что обычай readObject а также writeObject методы ничего не делают, но вызывают стандартные процедуры чтения / записи, не предоставляя никакой настройки, но это, вероятно, было сделано для примера.)

Этот код небезопасен? Нет, не совсем. Это просто не будет работать так, как было задумано.

Пользователь Powerlord спросил, может ли злоумышленник просто позвонить Ser.readObject(myInputStream), Это не сработает, потому что defaultReadObject метод разрешается вызывать только при десериализации объекта. Если это не так, он бросит NotActiveException,

Исправление для этого кода состоит в том, чтобы изменить методы так, чтобы они private методы экземпляра вместо public static методы. Это заставит механизм сериализации вызывать эти методы, чтобы они могли реализовать некоторые настройки формата.

Однако ссылка на статью неверна, если в ней говорится, что это не позволит злоумышленникам создавать нежелательные экземпляры. Правильно, что приватный конструктор будет обойден десериализацией. Но делая readObject а также writeObject private не помешает злоумышленникам десериализовать столько экземпляров этого класса, сколько они захотят. Вероятно, это то, что беспокоило Powerlord. Я мог бы легко составить свой собственный поток байтов и десериализовать из него объекты, сколько угодно раз, с любым содержимым, которое я выберу:

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

Чтобы предотвратить это, Ser класс должен был бы реализовать некоторые проверки в readObject и бросить что-то вроде InvalidObjectException если бы он хотел предотвратить десериализацию. Другой подход был бы для его реализации readResolve и вернуть уже созданный экземпляр (например, синглтон) вместо создания нового.

Для дальнейшего обсуждения см. Bloch, Effective Java, пункты 74-78. Обратите внимание, что он рекомендует использовать enum вместо readResolve если вы хотите синглтон.

Сайт неверный. Спецификация Сериализации Объекта ясно заявляет, что подпись writeObject() является

private void writeObject(ObjectOutputStream stream)
    throws IOException;

и так же, что подписьreadObject() является

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

Если вы делаете их public или же static они не будут называться. Здесь они оба.

Другие вопросы по тегам