item 90) 직렬화된 인스턴스 대신 직렬화 프록시 사용을 검토하라
직렬화 프록시 패턴
Serializable의 버그와 보안 문제 위험을 줄여주는 기법
직렬화 프록시 구현 방법
바깥 클래스의 논리적 상태를 정밀하게 표현하는 중첩 클래스(직렬화 프록시 클래스)를 설계해 private static으로 선언한다.
중첩 클래스의 생성자는 단 하나여야 하며, 바깥 클래스를 매개변수로 받는다.
중첩 클래스의 생성자는 단순히 인수로 넘어온 인스턴스를 복사한다. (일관성 검사 및 방어적 복사 필요 X)
바깥 클래스와 직렬화 프록시 모두 Serializable을 선언한다.
다음은 직렬화 프록시 클래스의 예시이다.
writeReplace
직렬화 프록시 사용하는 바깥 클래스에 writeReplace 메서드를 작성한다.
직렬화가 이뤄지기 전 바깥 클래스 인스턴스 대신 프록시 인스턴스를 반환하도록 한다.
readObject
직렬화 프록시 사용하는 바깥 클래스에 readObject 메서드를 작성한다.
불변식을 훼손하려 공격이 들어와도 바깥 클래스의 직렬화된 인스턴스를 생성할 수 없다.
readResolve
역직렬화 시 직렬화 시스템이 프록시 인스턴스를 바깥 클래스의 인스턴스로 변환하게 해줘야 한다.
일반 인스턴스 생성할 때와 똑같은 생성자, 정적 팩토리 등을 사용해 역직렬화된 인스턴스를 생성한다.
장점
가짜 바이트 스트림 공격이나 내부 필드 탈취 공격을 프록시 수준에서 차단할 수 있다.
필드를 final로 선언해도 되므로 직렬화 시스템에서 진정한 불변으로 만들 수 있다.
역직렬화 시 유효성 검사를 수행하지 않아도 된다.
역직렬화한 인스턴스와 원래의 직렬화된 클래스가 달라도 정상적으로 동작한다.
원소 개수에 따라 다른 타입을 사용하는 EnumSet의 예시
원소가 64개 이하면 RegularEnumSet을 그보다 크면 JumboEnumSet을 반환한다.
만약 64개짜리 열거 타입을 가진 EnumSet을 직렬화한 다음 원소 5개를 추가하고 역직렬화하면, 처음 직렬화된 것은 RegularEnumSet이지만 역직렬화될 때는 JumboEnumSet 인스턴스를 반환할 것이다.
한계점
클라이언트가 마음대로 확장할 수 있는 클래스에는 적용할 수 없다.
객체 그래프에 순환이 있는 클래스에 적용할 수 없다. 실제 객체는 아직 만들어지지 않아 readResolve 내에서 메서드 호출 시 ClassCastException이 발생한다.
방어적 복사보다는 속도가 느리다.
Last updated