[CS] 이펙티브 자바 독서 #3 - 아이템85. 직렬화를 피하라!

2025. 10. 23. 23:58·언어공부/Java | Kotlin

 

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

https://www.yna.co.kr/view/AKR20161129029400091

 

샌프란시스코 경전철 '랜섬웨어' 공격으로 결제시스템 마비 | 연합뉴스

(샌프란시스코=연합뉴스) 김현재 특파원 = 미국 캘리포니아주 샌프란시스코의 경전철 운영시스템이 해커의 공격을 받아 지난 주말 결제시스템이 마비됐...

www.yna.co.kr

2016년 샌프란시스코 교통국이 렌섬웨어에 감염되어 열차 발권, 배차 시스템이 정지되었다.

 

이 문제의 원인은 바로 직렬화.

직렬화의 가장 근본적인 문제는 역직렬화에 있다. 역직렬화의 readObject 메서드는 그 객체가 어느 객체인지간에 Serializable의 구현체라면 다시 객체를 만들어낼 수 있다. 모종의 사유로 역직렬화 코드가 실행될 때 해커가 악성코드를 심어놨다면, 그 코드도 같이 실행되는 것이다.

 

문제는 자바의 기본 라이브러리에서도 역직렬화가 간접적으로 사용될 수 있다. 신뢰할 수 없는 스트림을 역직렬화하면 악성코드 및 보안문제에 취약해진다.


굳이 악성코드를 심지 않아도 위험하다

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);
    }
    return serialize(root); //serialize 코드는 생략함
}

위 코드에서의 HashSet 객체는 반복문에서 생성되어 s1, s2에 더해지는 t1 100개 t2 100개씩해서 200개, root와 s2 2개해서 202개의 HashSet 객체가 생성된다. (책에는 201개라 되어있는데 뭐가 맞는지는 잘 모르겠다.) 202개의 HashSet 인스턴스는 3개 이하의 객체참조를 가지게 된다.

 

위 코드를 직렬화한 다음 어딘가에서 불러와 역직렬화를한다고하면

맨처음 root HashSet을 역직렬화하려면 그 원소의 해시코드를 계산해야하는데, 루트 HashSet은 두 개의 HashSet을 인스턴스로 가지고 있고, 그 두 개의 HashSet은 새로운 루트 HashSet이 되어 또 2개의 HashSet을 가지고... 이렇게 반복문을 100번 실행했으므로 HashSet을 역직렬화하기 위해서는 HashCode (해시코드를 구하는 알고리즘)을 2^100번 실행해야할 것이다. 영원히

 

문제는 역직렬화가 잘못되어도 아무 알림도 오지 않고, 스택 오버플로우 에러도 일어날 것이다. 만약 악의를 품고 이 코드를 심어놨다면 그 코드를 실행한 서버는 다운될 것이고. 이렇게 역직렬화 시간이 오래걸리는 메서드를 여러 번 수행하도록 만드는 것을 역직렬화 폭탄이라고 한다.


그렇다면 이러한 역직렬화의 취약점을 보완할 수 있는 방법이 있을까? 신뢰할 수 없는 데이터를 역직렬화하는 것은 클라이언트의 의도에 100% 따르게된다.

 

가장 좋고 안전한 역직렬화 방법은 역직렬화를 하지 않는 것이다. 자바에서 직렬화를 사용하지 말아라. 역직렬화도 사용하지 말아라. 직렬화와 역직렬화가 주로 수행하는 객체 ↔ 바이트 시퀀스 간 컨버터 역할을 다른 방법으로도 가능하다. <이펙티브 자바>에서는 '크로-플랫폼 구조화된 데이터 표현 (cross-platform structured-data representation)'이라 칭한다. 사실 이것 역시 직렬화 시스템으로 불려도 무방하다.


이러한 표현들은 직렬화/역직렬화를 수행하지 않는다. 대신 key-value 형식으로 간단하게 구조화된 데이터 객체를 사용한다. 타입도 기본형 타입과 배열만을 지원한다. 즉, 직렬화/역직렬화에 비해 훨씬 간단하며 직렬화의 문제들을 회피할 수 있다.

 

key-value 형식으로 구조화되며 기본형 타입과 배열만을 지원하는 데이터 객체 중 가장 익숙하며 가장 유명한 것이 바로 JSON 이다. JSON은 JavaScript Object Notation (자바스크립트 객체 표기법) 으로 자바스크립트용도로 설계되었다. 자연어로 구성되어 사람이 읽기 매우 편하다.

 또 하나의 예시는 프로토콜 버퍼다. C++ 용도로 만들어졌으며 이진표현을 제공하기 때문에 효율성이 높다. 프로토콜 버퍼는 문서를 위한 스키마(타입)을 제공하고 이를 준수하도록 한다.


그래도 만약 직렬화가 꼭 필요하다면 신뢰할 수 없는 데이터를 절대 역직렬화를 해서는 안된다는 것을 명심해야한다.

자바에서도 이를 인지하고 있어서 JDK9부터는 역직렬화 필터링을 제공한다. 클래스 단위로 특정 클래스를 받아들이거나 거부할 수 있으며, 화이트 리스트에 등재된 클래스만을 받도록 설정할 수도 있다. 다만 역직렬화 폭탄은 피할 수 없으므로 주의해야한다.

'언어공부 > Java | Kotlin' 카테고리의 다른 글

[CS] 자바의 정석 독서 #16 - String 클래스 파헤치기  (0) 2025.11.18
[CS] 자바의 정석 독서 #14 - 예외처리  (0) 2025.11.11
[CS] 스레드란 무엇일까?  (0) 2025.10.15
[CS] 이펙티브자바 독서 #2 - 아이템18  (0) 2025.10.14
[CS] 이펙티브자바 독서 #1 - 아이템15 ~ 17  (0) 2025.10.11
'언어공부/Java | Kotlin' 카테고리의 다른 글
  • [CS] 자바의 정석 독서 #16 - String 클래스 파헤치기
  • [CS] 자바의 정석 독서 #14 - 예외처리
  • [CS] 스레드란 무엇일까?
  • [CS] 이펙티브자바 독서 #2 - 아이템18
Radiata
Radiata
개발을 합니다.
  • Radiata
    DDD
    Radiata
  • 전체
    오늘
    어제
    • 분류 전체보기 (211)
      • 신년사 (3)
        • 2025년 (2)
        • 2026년 (1)
      • CS (59)
        • JVM (12)
        • 백엔드 (20)
        • 언어구현 (1)
        • 객체지향 (1)
        • 논리회로 (5)
        • 컴퓨터구조 (9)
        • 데이터베이스 (1)
        • 컴퓨터 네트워크 (10)
      • 언어공부 (64)
        • Java | Kotlin (48)
        • JavaScript | TypeScript (9)
        • C | C++ (6)
      • 개인 프로젝트 (11)
        • [2025] Happy2SendingMails (3)
        • [2026] 골든리포트! (8)
        • [2026] 순수자바로 개발하기 (0)
        • 기타 이것저것 (0)
      • 팀 프로젝트 (29)
        • [2025][GDG]홍대 맛집 아카이빙 프로젝트 (29)
      • 알고리즘 (13)
        • 백준풀이기록 (11)
      • 놀이터 (0)
      • 에러 수정일지 (2)
      • 고찰 (24)
        • CEOS 23기 회고록 (2)
  • 블로그 메뉴

    • CS
    • 언어공부
    • 개인 프로젝트
    • 팀 프로젝트
    • 알고리즘
    • 고찰
    • 신년사
    • 컬러잇 개발블로그
  • 링크

    • 컬러잇 개발블로그
  • 공지사항

  • 인기 글

  • 태그

    144
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
Radiata
[CS] 이펙티브 자바 독서 #3 - 아이템85. 직렬화를 피하라!
상단으로

티스토리툴바