Skip to content

[Item88] readObject 메서드는 방어적으로 작성하라 #142

Description

@imsando

## 목표

  • 역직렬화 과정에서 불변성이 깨지는 문제를 방지하는 방법을 알아보자.

## 핵심 요약

직렬화와 역직렬화

  • 직렬화
    자바에서 객체를 파일에 저장하거나, 네트워크로 보내려면 숫자나 문자처럼 변환하는 과정을 직렬화라고 함.
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data.obj"));
    out.writeObject(new Person("ssddo", 25));  // 객체를 파일로 저장
    out.close();
  • 역직렬화
    파일에서 다시 객체로 복원하는 과정을 역직렬화라고 함.
    ObjectInputStream in = new ObjectInputStream(new 
    FileInputStream("data.obj"));
    Person p = (Person) in.readObject();  // 파일에서 객체로 복원
    in.close();

readObject란?

  • readObject(ObjectInputStream s)는 객체를 파일에서 읽어올 때 자동으로 실행되는 메서드다.
  • "숨겨진 생성자"처럼 작동해서 객체를 복원하는 역할을 한다.
  • 하지만 보안 문제가 발생할 가능성이 있다.

1️⃣ readObject의 위험성

  • readObject는 사실상 "바이트 스트림을 받는 또 다른 생성자" 역할을 한다.
  • 따라서 기존 생성자에서 수행하는 불변성 유지 및 방어적 복사가 반드시 필요하다.
  • 그렇지 않으면 악의적인 직렬화 데이터를 이용한 공격이 가능해진다.

2️⃣ readObject를 사용할 때 주의할 점

🧩 방어적 복사를 하자
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
    s.defaultReadObject();  // 기본 역직렬화 수행

    // 🔒 원본을 보호하기 위해 새로운 객체로 복사
    start = new Date(start.getTime());
    end = new Date(end.getTime());

    // 🚨 유효성 검사 (잘못된 값 방지)
    if (start.compareTo(end) > 0) {
        throw new InvalidObjectException("시작 날짜가 종료 날짜보다 늦을 수 없습니다!");
    }
}
  • Date, ArrayList 같은 가변 객체는 외부에서 값을 바꿀 수 있다.
  • 따라서 위의 예시처럼 readObject에서 새로운 복사본을 만들어 원본을 보호해주어야 한다.
🧩 불변식을 검사하여 유효하지 않은 객체 생성 시 예외를 던져라
  • InvalidObjectException을 사용해 객체 생성 자체를 막을 수 있다. (위 예시)
🧩 readObject 내부에서 절대 재정의 가능한 메서드를 호출하지 말라
  • readObject 실행 중 하위 클래스의 오버라이딩 메서드가 호출되면 예측할 수 없는 동작이 발생할 수 있다.

💡 핵심 정리

  • readObject새로운 생성자와 같으므로, 불변성을 유지할 수 있도록 방어적으로 작성해야 한다.
  • 가변 객체는 반드시 방어적 복사(Defensive Copy) 를 수행해야 한다.
  • InvalidObjectException을 활용해 불변식 위반을 사전에 차단하라.
  • 재정의 가능한 메서드를 호출하지 않도록 주의하라.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions