[Effective Java] 방어 가능한 readObject 메소드를 작성하자 |
-
Serializable 하게 만들고 싶은 class 의 물리적 표현과 논리적 표현이 같다고 해도,
무조건 implements Serializable 을 붙이는 것이 능사가 아니다.
readObject 는 바이트 스트림 인자 하나만 받는 생성자라고 볼 수 있는데
누군가가 고의적으로 이상한 바이트 스트림을 제공할 경우 문제가 될 수 있다.
따라서 readObject 메소드를 만들고, defaultReadObject() 를 수행 후, 역직렬화되는 객체의 유효성을 검사해야 한다.
만일 유효성 검사에 실패하면, readObject 메소드에서 InvalidObjectException 예외를 발생시켜야 한다.
-
위의 방법으로 유효성 문제는 해결할 수 있지만, 유효성은 지키되 reference 참조하여 값을 바꿀 수 있는 형식의 값이 들어오면 문제가 될 수 있다. ( stream 에 고의적으로 불변이어야 하는 어떤 녀석의 객체 참조를 public 형태로 갖도록 할 수 있다. )
따라서 객체를 역직렬화 할 때는 클라이언트가 소유하면 안 되는 객체 참조를 포함하는 모든 필드를 방어 복사하는 것이 중요하다.
readObject 는 실제 생성자가 아니기 때문에 방어복사를 할 때 final 로 선언할 수 없다.
-
writeUnshared, readUnshared 메소드는 방어 복사에 관련된 함수들인데 공격에는 취약하기에 사용하지 말자.
빠르지만 안전을 보장하지 않는다.
-
기본으로 제공되는 readObject 가 우리 클래스에 적합한지 결정하려면 다음의 테스트를 해야 한다.
인자를 받는 public 생성자가 있고, 이 생성자에서 인자에 대한 아무런 검증도 하지 않고, 그 인자 값을 transient 가 아닌 인스턴스 필드에 그대로 저장해도 문제가 없는가?
만약 이 검사에 문제가 있다면, readObject 메소드를 제공해서 그 메소드가 모든 유효성 검사를 수행하고 그 인자들을 방어복사하도록 해야 한다.
다른 방법으로는 직렬화 프록시 패턴(Serialization Proxy Pattern) 이 있다.
-
final 이 아닌 직렬화 가능 클래스와 관련하여 readObject 메소드와 생성자 간에는 한 가지 유사한 점이 있다.
readObject 메소드에서는 직접 또는 간접이건 오버라이딩이 가능한 메소드를 호출하면 안 된다.
만일 이 규칙을 위반하고 호출할 메소드가 오버라이딩 되면, 서브 클래스의 상태가 완전히 역직렬화 되기 전에 오버라이딩한 메소드가 실행될 수 있고, 실패하기 쉽다.
Summary
readObject 메소드를 작성할 때는 언제든지 public 생성자를 만든다고 생각하자.
역직렬화 시에 주어지는 바이트 스트림이 실제 직렬화된 인스턴스를 나타낸다고 단정짓지 말자.
기본 직렬화 뿐만 아니라 독자적인 직렬화에도 똑같이 적용되는 이야기이다.
어떤 공격에도 뚫리지 않는 readObject 메소드를 작성하는 지침은 다음과 같다.
- 외부에 공개되지 않아야 하는 객체 참조 필드를 갖는 클래스의 경우에는, 그런 필드가 참조되는 각 객체를 방어 복사하자. 불변 클래스에 포함된 가변 컴포넌트가 이런 부류이다.
- 어떤 불변 규칙도 모두 검사하고 검사가 실패하면 InvalidObjectException 예외를 던지자. 검사는 방어 복사 전에 해야 한다.
- 만일 특정 객체가 역직렬화 된 후에 그 객체의 전체 객체 그래프를 검사해야 한다면, ObjectInputValidation 인터페이스를 사용하자.
- 직접 또는 간접이건, 오버라이딩 가능한 메소드를 호출하지 말자.
'프로그래밍 놀이터 > 디자인 패턴, 리펙토링' 카테고리의 다른 글
[Effective Java] 직렬화된 인스턴스 대신 직렬화 프록시의 사용을 고려하자. (0) | 2017.03.28 |
---|---|
[Effective Java] 인스턴스 제어에는 readResolve 메소드보다 enum 타입을 사용하자. (0) | 2017.03.27 |
[Effective Java] 독자적인 직렬화 형태의 사용을 고려하자 (0) | 2017.03.23 |
[Effective Java] Serializable 인터페이스를 분별력 있게 구현하자. (0) | 2017.03.21 |
[Effective Java] 스레드 그룹을 사용하지 말자. (0) | 2017.03.20 |
댓글