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
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions