cleanUrl: /posts/consider-serialization-proxies-instead-of-serialized-instances

앞선 chapter 에서 버그와 보안 문제가 일어날 가능성이 있음을 확인하였는데 이 위험을 크게 줄일 방법이 serialization proxy pattern 이다.

Period 를 예시로 든다.

private static class SerializationProxy implements Serializable {

    private static final long serialVersionUID = -2785633062946028119L;

    private final Date start;
    private final Date end;

    SerializationProxy(Period period) {
        start = period.start;
        end = period.end;
    }
}

이 다음에 외부 클래스(Period)에 writeReplace 를 추가한다. 이건 그냥 복사해서 어디에서든지 사용해도 괜찮다.

private Object writeReplace() {
		return new SerializationProxy(this);
}

목적은 Period 의 직렬화를 호출하면 결국 내부의 SerializationProxy 를 반환하는 역할을 한다.

직렬화가 이뤄지기 전에 바깥 클래스의 instance 를 직렬화 proxy 로 변환한다

writeReplace 때문에 직렬화 시스템은 결코 바깥 클래스의 직렬화 instance 를 생성할 수 없다.

다음의 readObject 를 바깥 클래스에 추가하면 공격자가 불변식을 훼손하고자 하는 시도 역시 막을 수 있다

// 직렬화 proxy pattern
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
    throw new InvalidObjectException("proxy 가 필요합니다");
}

마지막으로 바깥 클래스와 동일한 instance 를 반환하도록 readResolve 를 내부에 추가한다

private Object readResolve() {
    return new Period(start, end);
}

이 method 드는 공개된 API 만을 사용해 바깥 클래스의 instace 를 생성한다.