Adventure Time - Lady Rainicorn [아이템 85] 자바 직렬화의 대안을 찾으라
본문 바로가기
🤓 스터디/이펙티브 자바

[아이템 85] 자바 직렬화의 대안을 찾으라

by 강켄트 2023. 5. 2.

 

💡직렬화(Serializable)란?

어떤 데이터를 다른 데이터의 형태로 변환하는 것인데,

책에서는 객체의 상태를 바이트 스트림으로 변환하는 것을 의미한다.

(바이트 스트림에서 객체의 상태로 변환하는 것은 역직렬화(Deserializable)이다.)

 

💡바이트 스트림이란?

1byte(컴퓨터에서 기본으로 처리되는 최소 단위)를 입출력할 수 있는 스트림이다.

자바에서 입출력 스트림을 통해 흘러가는 데이터의 기본단위이고

일반적으로 바이트로 구성된 파일, 즉 동영상, 이미지, 음악 파일을 처리하기 적합한 스트립이다.

(클래스 이름에 Stream이 붙으면 바이트 스트림을 처리하기 위한 클래스)

출처 - https://codedragon.tistory.com/3535

 

 

이번 아이템 주제는 딱 한 문장으로 요약된다.

"... 여러분이 작성하는 새로운 시스템에서 자바 직렬화를 써야 할 이유는 전혀 없다."

 

 

직렬화의 근본적인 문제

공격 범위가 너무 넓고 지속적으로 더 넓어져서 방어하기 어렵다.

ObjectInputStream의 readObject 메서드를 호출하면서 객체 그래프가 역직렬화되기 때문이다.

readObject 메서드는 클래스패스 안의 거의 모든 타입의 객체를 만들어 낼 수 있는데,(반환 타입이 Object)

바이트 스트림을 역직렬화하는 과정에서 이 메서드는 그 타입들 안의 모든 코드를 수행할 수 있게 된다.

즉, 그 타입들의 코드 전체가 공격 범위에 들어가는 것이다. 

 

역직렬화에 시간이 오래 걸리는 짧은 스트림을 역직렬화하는 것만으로도 서비스 거부 공격에 노출될 수 있다.

다음은 역직렬화 폭탄(deserialization bomb)의 예이다.

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); // 직렬화 수행
}

 

해결

애초에 신뢰할 수 없는 바이트 스트림을 역직렬화하는 일 자체가 스스로를 공격에 노출하는 행위다.

✅ 직렬화 위험을 회피하는 가장 좋은 방법은 아무것도 역직렬화하지 않는 것이다.

✅ 직렬화를 피할 수 없고 역직렬화한 데이터의 안전을 확신할 수도 없을 때는 역직렬화 필터링(ObjectInputFilter)을 사용하자.

      객체 역직렬화 필터링은 데이터 스트림이 역직렬화되기 전에 필터를 설치하는 기능이다.

      클래스 단위로, 특정 클래스를 받아들이거나 거부할 수 있다.

 

정리

직렬화란 객체의 상태를 바이트 스트림으로 변환하는 것

신뢰할 수 없는 바이트 스트림의 역직렬화는 하지 말자

꼭 역직렬화를 해야 한다면 ObjectInputFilter를 사용하자

댓글