객체 직렬화란 자바가 객체를 바이트 스트림으로 인코딩하고 (직렬화) 그 바이트 스트림으로부터 다시 객체를 재구성하는(역직렬화) 매커니즘이다.
직렬화된 객체는 다른 VM에 전송하거나 디스크에 저장한 후 나중에 역직렬화 할 수 있다.
이번 장은 직렬화가 품고 있는 위험과 그 위험을 최소화하는 방법에 집중한다.
1997년, 자바에 처음으로 직렬화가 도입되었다.
그 때만 하더라도 직렬화는 연구용 언어인 모듈라-3에서나 시도되었을 뿐, 대중적 언어에 적용된건 처음이었기에 다소 위험하지 않겠냐는 이야기가 나왔다.
프로그래머가 어렵지 않게 분산 객체를 만들 수 있다는 구호는 매력적이었지만, 보이지 않는 생성자, API와 구현 사이의 모호해진 경계, 잠재적인 정확성 문제, 성능, 보안, 유지보수성 등 그 대가가 컸다.
지지자들은 장점이 이런 위험성을 압도한다고 생각했지만, 지금까지 경험한 바로는 그 반대다.
보안 문제가 심각한 것으로 밝혀졌다 (샌프란시스코 시영 교통국이 랜섬웨어 공격을 받아 요금 징수 시스템이 이틀간 마비)
직렬화의 근본적인 문제는 공격 범위가 너무 넓고 지속적으로 더 넓어져 방어하기 어렵다는 점이다.
ObjectInputStream의 readObject 메서드를 호출하면서 객체 그래프가 역직렬화 되기 때문이다.
readObject 메서드는 (Serialiable 인터페이스를 구현했다면) 클래스패스 안의 거의 모든 타입의 객체를 만들어 낼 수 있는, 사실상 마법 같은 생성자다.
바이트 스트림을 역직렬화 하는 과정에서 이 메서드는 그 타입들 안의 모든 코드를 수행할 수 있다.
이 말인즉슨, 그 타입들의 코드 전체가 공격 범위에 들어간다는 뜻이다.
역직렬화 과정에서 호출되어 잠재적으로 위험한 동작을 수행하는 메서드를 가젯이라 부른다.
여러 가젯들이 모여 체인을 이룰 수도 있다. (아주 위험해질 수도 있음)
그래서 아주 신중하게 제작한 바이트 스트림만 역직렬화해야 한다.
역직렬화에 시간이 오래 걸리는 짧은 스트림을 역직렬화 하는 것만으로도 서비스 거부 공격에 쉽게 노출될 수 있다 ⇒ 역직렬화 폭탄
HashSet 인스턴스를 역직렬화 하려면 그 원소들의 해시코드를 계산해야 하는데, 매우 오래걸리기 때문에 시도해서는 안 된다.
애초에 신뢰할 수 없는 바이트 스트림을 역직렬화하는 일 자체가 스스로를 공격에 노출하는 행위다.