Search

Item 85. 자바 직렬화의 대안을 찾으라

생성일
2023/08/24 03:40
챕터
12장 - 직렬화

요약

역직렬화는 수많은 위험성과 단점들을 포함하고 있다.
역직렬화 대신 다른 포맷을 쓰거나, 역직렬화 필터링을 사용하여 신뢰할 수 있는 클래스만 역직렬화 하자.

역직렬화의 위험성

자바 직렬화는 쉽게 분산 객체를 만들 수 있지만, 생성자, API와 구현의 모호한 경계, 잠재적인 정확성 문제, 성능, 보안, 유지보수성의 문제를 가지고 있다.
직렬화의 근본적인 문제는 공격 가능한 범위가 너무 넓어 방어하기가 어렵다는 점이다. ObjectInputStream의 readObject는 역직렬화를 해주는데, 사실상 classpath 내에 모든 타입의 객체를 만들어 낼 수 있는 생성자이다.
역직렬화를 한다는 것은 위의 classpath 내의 모든 타입들의 코드 전체가 공격 범위에 들어간다는 것이다.
역직렬화 과정에서 호출되어 잠재적으로 위험한 동작을 수행하는 메서드를 가젯(gadget)이라 부르는데, 여러 가젯을 함께 사용하여 가젯 체인을 구성할 수도 있다.
이런 가젯 체인으로 공격자가 기반 하드웨어의 네이티브 코드를 마음대로 실행시켜 위험에 노출시킬 수 있다.
가젯을 사용하지 않더라도 아래와 같이 짧은 코드로도 역직렬화가 영원히 끝나지 않게 만들어 서비스 거부 공격을 만들어 낼 수 있다.
static byte[] bomb() { Set<Object> root = new HashSet<>(); Set<Object> s1 = root; Set<Object> s2 = new HashSet<>(); for (int i = 0; i < 100; i++) { Set<Object> t1 = new HashSet<>(); Set<Object> t2 = new HashSet<>(); t1.add("foo"); s1.add(t1); s1.add(t2); s2.add(t1); s2.add(t2); s1 = t1; s2 = t2; } return serialize(root); }
Java
복사
위 코드는 201개의 HashSet 인스턴스로 구성되는데, 역직렬화 시에 HashSet 인스턴스의 해시코드를 계산해야한다.
그 과정에서 100개의 depth를 가지는 HashSet의 해시코드를 계산하는 것이 끝나지 않고 계속되며, 어떤 문제가 발생했다는 신호조차 주지 않는다.

직렬화 대안

이러한 직렬화에 내재된 위험들을 피하는 가장 좋은 방법은 아무것도 역직렬화 하지않는 것이다.
자바 직렬화보다 간단하고 다양한 플랫폼 지원, 우수한 성능, 풍부한 지원 도구 등 수많은 이점을 가진 포맷들을 사용하자.
대표적인 예로 텍스트 기반이라 사람이 읽을 수 있는 JSON과 이진 표현이라 효율이 높은 프로토콜 버퍼(Protocol Buffers)가 있다.
그래도 직렬화를 사용해야한다면, 신뢰할 수 있는 데이터만 역직렬화 해야한다.
데이터가 안전한지 확신할 수 없다면 객체 역직렬화 필터링을 사용하자.
ObjectInputFilter.REJECTED / ALLOWED를 통해 구현할 수 있다.
역직렬화 필터링은 데이터 스트림이 역직렬화 되기 전에 필터를 설치하는 기능으로, 클래스 단위로 해당 클래스를 받아들이거나 거부할 수 있다.
기본 수용 모드에서 블랙리스트의 클래스들을 거부하는 방식과, 기본 거부 모드에서 화이트리스트의 클래스들만 받아들이는 방식이 있다.
블랙리스트 방식보다 화이트리스트 방식이 권장된다.