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

2022-04-01 19:24:41

#Java#Effective Java 3/E

직렬화, 역직렬화

  • 직렬화 : Java가 객체를 byte stream으로 인코딩
  • 역직렬화 : byte stream을 객체로 encoding

역직렬화와 보안간의 관련성

  • 직렬화의 근본적인 문제는 공격범위가 넓다는 점이다. ObjectInputStream의 readObject method를 호출하면 객체 그래프가 역직렬화되기 때문이다. 클래스패스 안의 Serilizable interface를 구현한 거의 모든 타입의 객체를 만들어낼 수 있기 떄문에, 바이트 스트림을 역직렬화하는 과정에서 이 method는 그 타입들 안의 모든 코드를 수행할 수 있다. --> 그 타입들의 코드 전체가 공격범위에 들어간다.

  • gadget method : 객체 역직렬화 과정에서 호출되어 잠재적으로 위험한 동작을 수행하는 method

따라서 위험요소가 없는 byte stream만 역직렬화해야 한다.

그렇다하더라도 역직렬화에 시간이 오래걸리는 짧은 stream을 역직렬화 하는 것만으로도 서비스 거부 공격(denial-of-service,DoS)을 할 수 있다.

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);
}

위 코드는 201개의 HashSet 객체를 만드는데, 각각은 3개 이하의 객체 참조를 가진다. HashSet을 역직렬화하려면 각각 원소의 해시코드를 계산해야 하는데, HashSet에 포함된 객체 참조 타입이 역시 HashSet임으로 depth가 100단계까지 만들어진다. 즉 2^100 이상으로 HashCode method를 호출해야 한다.

역직렬화의 보안성 이슈를 피하는 방법

  • 저자는 역직렬화 자체를 하지 않을 것을 권고하고 있다. 직렬화 대신에 객체를 byte sequence로 바꿔주는 mechanism (cross-platform structured-data representation)을 사용하자
  • 만약 직렬화를 피할수 없는 상황이고, 역직렬화한 데이터가 안전한지 완전히 확신할 수 없다면, 객체 역직렬화 필터링 (java.io.ObjectInputFilter)을 사용하자 이 기능은 data stream이 역직렬화되기 전에 필터를 설치하는 기능이다. 클래스 단위로 특정 클래스를 받아들어거나 거부할 수 있는데, 저자는 블랙 리스트 방식보다는 안전하다고 알려진 클래스만 허용하는 화이트 리스트 방식을 사용할 것을 권고하고 있다.

Cross-platform structured-data representation

  • 객체를 byte sequence 로 바꿔주는 메카니즘
  • 객체 그래프는 직렬화 / 역직렬화하지 않고, 간단한 K-V형태의 객체를 사용한다.
  • 대표적인 예시로 JSON과 protocol buffer가 있다.
  • JSON은 텍스트 기반이라 사람이 읽을 수 있고, Protocol buffer는 이진 표현이라 효율이 높다.
프로필 이미지
@chani
바둑 좋아하는 개발자의 의미있는 학습 기록을 위한 공간입니다.

댓글

이 게시글에 대한 의견을 공유해주세요!

댓글