88. readObject 메서드는 방어적으로 작성하라
0 시작 하기 전, readObject와 역직렬화 핵심 요약
readObject와 역직렬화 핵심 요약%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#f0f8ff', 'primaryTextColor': '#000', 'primaryBorderColor': '#7570b3', 'lineColor': '#333', 'edgeLabelBackground':'#fff', 'clusterBkg': '#fcfcfc'}}}%%
graph TD
subgraph 역직렬화 과정
A[ByteStream-->파일/네트워크] --> B{ObjectInputStream};
B -- readObject() 호출 --> C[객체 생성 프로세스];
C -- 기본 역직렬화 --> D[1.객체 메모리 할당<br>2.스트림 데이터로<br>필드 값 채우기<br>->생성자 코드 실행 안 함!];
C -- 커스텀 readObject() 구현 시 --> E[개발자가 정의한<br>readObject 메서드 실행];
D --> F[객체 복원 완료-잠재적 위험!];
E --> G[객체 복원 완료-안전하게 구현 시];
end
subgraph 클래스 정의
H[MyClass] -- 구현 --> I(Serializable);
H -. 정의 가능 .-> J["private void readObject(...)"];
end
style F fill:#ffcccc,stroke:#a00
style G fill:#ccffcc,stroke:#0a0
style J fill:#ccf,stroke:#333,stroke-width:2px핵심 포인트
1. 왜 readObject는 방어적으로 작성해야 하는가? 🤔 (기본 역직렬화의 위험성)
readObject는 방어적으로 작성해야 하는가? 🤔 (기본 역직렬화의 위험성)핵심 문제: 역직렬화는 객체 생성의 '다른 경로'이며, 기본적으로 생성자의 안전장치를 우회
정상적인 입주(객체 생성) 절차
특별한 '뒷문'(readObject 메커니즘)을 사용
readObject 메커니즘)을 사용바로 이 점이 문제
발생 가능한 공격 유형 (뒷문으로 들어오는 위험)
1. 공격 1: 불변식(Invariant) 깨뜨리기
원리
결과
2. 공격 2: 불변성(Immutability) 깨뜨리기
원리:
결과:
기억하세요
2. 미흡한 해결책: 유효성 검사만으로는 부족하다! 🤔
시도: readObject 메서드를 구현하여 defaultReadObject() 호출 후, 생성자처럼 유효성 검사 로직만 추가.
readObject 메서드를 구현하여 defaultReadObject() 호출 후, 생성자처럼 유효성 검사 로직만 추가.문제점:
공격 1(규칙 위반 객체 생성)은 방어 가능
공격 2(내부 상태 변경)는 방어 불가
3. 해결책: 방어적인 readObject 구현 (3단계) 💡
readObject 구현 (3단계) 💡목표: 어떤 종류의 바이트 스트림이 오더라도 객체의 불변식(규칙) 과 불변성(상태)을 모두 지키는 것.
핵심 전략: "일단 읽고 → 내부 보호(복사) → 최종 확인(검증)"
구현 단계:
1. s.defaultReadObject(); 호출 (1단계: 읽기):
s.defaultReadObject(); 호출 (1단계: 읽기):2. 내부 '변경 가능(Mutable)' 필드 방어적 복사 (2단계: 보호):
핵심!
이유
주의
3. 객체 상태 유효성 검사 (3단계: 확인):
개념 예시 (방어적 readObject):
readObject):장점: 악의적인 바이트 스트림 공격으로부터 객체의 규칙과 상태를 안전하게 보호 가능.
4. readObject 작성 시 반드시 지켜야 할 것들! 📌
readObject 작성 시 반드시 지켜야 할 것들! 📌1. readObject는 private으로 선언하세요.
readObject는 private으로 선언하세요.2. 어떤 바이트 스트림이든 처리 가능하게 만드세요.
3. 모든 '변경 가능(Mutable)' 내부 필드는 방어적으로 복사하세요. ✨
4. 유효성 검사는 방어적 복사 '이후'에 수행하세요. ✨
5. readObject 내에서 재정의 가능한(Overridable) 메서드를 호출하지 마세요! 🚫
readObject 내에서 재정의 가능한(Overridable) 메서드를 호출하지 마세요! 🚫이유
6. 모든 필드를 직접 읽고 유효성을 검사해야 한다면 defaultReadObject를 호출하지 않을 수도 있습니다.
defaultReadObject를 호출하지 않을 수도 있습니다.7. ObjectInputValidation 인터페이스 사용을 고려하세요.
ObjectInputValidation 인터페이스 사용을 고려하세요.5. 핵심 정리 ✨
Last updated