요약
•
역직렬화는 수많은 위험성과 단점들을 포함하고 있다.
•
역직렬화 대신 다른 포맷을 쓰거나, 역직렬화 필터링을 사용하여 신뢰할 수 있는 클래스만 역직렬화 하자.
역직렬화의 위험성
•
자바 직렬화는 쉽게 분산 객체를 만들 수 있지만, 생성자, 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를 통해 구현할 수 있다.
◦
역직렬화 필터링은 데이터 스트림이 역직렬화 되기 전에 필터를 설치하는 기능으로, 클래스 단위로 해당 클래스를 받아들이거나 거부할 수 있다.
◦
기본 수용 모드에서 블랙리스트의 클래스들을 거부하는 방식과, 기본 거부 모드에서 화이트리스트의 클래스들만 받아들이는 방식이 있다.
◦
블랙리스트 방식보다 화이트리스트 방식이 권장된다.