아이템 76. readObject 메서드는 방어적으로 구현하라
76. readObject 메서드는 방어적으로 구현하라
readObject
메서드는 바이트 스트림(byte stream)을 인자로 받는 일종의 생성자와 같다.
- 직렬화 된 바이트 코드를 Java 직렬화 명세서(Java Object Serialization Specification)를 참조하여 변조 후에
readObject
메서드로 읽으면, 원래 기대했던 객체 상태가 아닌 객체를 반환 할 수 있다. - 객체를 역으로 직렬화 할 때는 클라이언트가 가질수 없어야 하는 객체 참조를 담은 모든 필드를 방어적으로 복사해야 한다.
// 방어적 복사와 유효성 검사를 모두 시행하는 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(start + " after " + end); }
유효성 검사 이전에 방어적 복사를 시해하고 있음을 주의하자.
자바 1.4부터는 방어적 복사 없이도 악의적 객체 참조 공격을 막을 수 있도록 하기 위해 고안된
writeUnshared
와readUnshared
메서드가ObjectOutputStrame
에 추가되었다.writeUnshared
와readUnshared
메서드를 사용하지 마라. 방어적 복사를 하는 것보다 빠르긴 하겠지만 필요한 안전성을 제공하지 못한다. - 직렬화 프락시 패턴(serialization proxy pattern)을 사용하는 것도 한 방법이다.
결론
- 어떠한 스트림이 주어지더라도 유효한 객체가 생성될 수 있도록 제작해야 한다.
- 바이트 스트림이 실제로 직렬화된 객체일 것이라 가정하지 마라.
- 안전한
readObject
메서드를 구현하고자 할 때 따라야 하는 지침- private로 남아있어야 하는 객체 참조 필드를 가진 클래스는, 그런 필드가 가리키는 객체를 방어적으로 복사해야 한다. 변경 불가능 클래스의 변경 가능 컴포넌트가 이 범주에 해당됨
- 불변식을 검사해서 위반된 사실을 감지될 경우,
InvalidObjectException
을 던져라. 불변식 검사는 방어적 복사 이후에 시행 - 객체를 완전 역직렬화 한 다음에 전체 객체 그래프의 유효성을 검사해야 한다면,
ObjectInputValidation
인터페이스를 이용하라. - 직접적이건 간접적이건, 재정의 가능 메서드를 호출하지 마라.