Skip to content

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

@travelbeeee

Description

@travelbeeee

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

자바 직렬화

  • 자바 직렬화란 자바가 객체를 바이트 스트림으로 인코딩하고(직렬화) 그 바이트 스트림으로부터 다시 객체를 재구성하는(역직렬화) 메커니즘
  • 직렬화된 객체는 다른 VM에 전송하거나 디스크에 저장한 후 나중에 역직렬화 할 수 있다
  • 1997년에 자바에 처음으로 직렬화가 도입되었고, �지금까지 경험한 바로는 직렬화의 장점보다는 보이지 않는 생성자, API와 구현 사이의 모호해진 경계, 정확성 문제, 성능, 보안, 유지보수성 등 위험성이 더 크다
  • 직렬화의 근본적인 문제는 공격 범위가 너무 넓고 지속적으로 더 넓어져 방어하기 어렵다는 점
    • readObject 메서드는 클래스패스 안의 거의 모든 타입의 객체를 만들어낼 수 있는 마법 같은 생성자
    • -> 바이트 스트림을 역직렬화하는 과정에서 이 메서드는 그 타입들 안의 모든 코드를 수행할 수 있다.
    • --> 그 타입들의 코드 전체가 공격 범위에 들어간다!
  • 신뢰할 수 없는 스트림을 역직렬화하면 원격 코드 실행, 서비스 거부 �등의 공격으로 이어질 수 있다.
  • 잘못한 게 아무것도 없는 애플리케이션이라도 이런 공격에 취약해질 수 있다.

폭탄 예시

    static byte[] bomb() {
        Set<Object> rootSet = new HashSet<>();
        Set<Object> set1 = rootSet;
        Set<Object> set2 = new HashSet<>();

        for (int i = 0; i < 100; i++) {
            Set<Object> tempSet1 = new HashSet<>();
            Set<Object> tempSet2 = new HashSet<>();

            tempSet1.add("foo");

            set1.add(tempSet1);
            set1.add(tempSet2);

            set2.add(tempSet1);
            set2.add(tempSet2);

            set1 = tempSet1;
            set2 = tempSet2;
        }

        return serialize(rootSet); // 자세한 코드 생략
    }
  • rootSet 에 다른 HashSet 2개가 담기고, 그 HashSet들도 다른 HashSet을 2개씩 가지고 있는 깊이 100단계까지 만들어진 예시
  • 역직렬화하려면 2^100 번 hashCode 메서드를 호출해야한다.

결론

  • 직렬화는 위험하니 피해야 한다. 직렬화 위험을 회피하는 가장 좋은 방법은 아무것도 역직렬화하지 않는 것이다.
    • JSON, 프로토콜 버퍼 같은 대안을 사용하자.
  • 신뢰할 수 없는 데이터는 역직렬화하지 말자.
    • 현실적으로 꼭 해야 한다면 객체 역직렬화 필터링을 사용하자 ( 자바 9에서 지원해주는 기능)
      • 객체 역직렬화 필터링도 모든 공격을 막아줄 수는 없음을 기억하고, 꼭 그렇게 만들어야 한다면 정말 신경써서 작성해야 한다.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions