자바 언어·플랫폼 용어 사전
실무 사례deserialization · 가젯 체인 · readObject · Serializable

역직렬화 취약점

신뢰할 수 없는 바이트를 객체로 되살리면 클래스패스의 코드가 연쇄 실행될 수 있다.

신뢰할 수 없는 바이트를 객체로 되살리는 것 자체가 코드 실행 통로가 된다.

왜 위험한가

자바 역직렬화는 단순히 필드를 채우는 것이 아니다

  • readObject() · readResolve() 같은 훅이 호출된다
  • 그 안에서 다른 객체의 메서드가 연쇄로 불린다

공격자는 클래스패스에 이미 있는 클래스들을 조합해 원하는 동작에 도달하는 경로(가젯 체인) 를 만든다

  • 새 코드를 주입할 필요가 없다. '있는 것' 만으로 충분하다
    • commons-collections 의 InvokerTransformer 가 대표적 가젯이었다

검증으로는 못 막는 이유

"역직렬화한 뒤에 검사하겠다" → 이미 늦었다 객체를 만드는 과정 자체에서 코드가 돈다

"타입을 확인하겠다" → 스트림을 읽어야 타입을 알 수 있다

  • 읽는 동안 이미 실행된다

방어

  • ① 신뢰 못 할 입력을 자바 직렬화로 받지 않는다 (근본 대응)
    • JSON · Protobuf 처럼 '데이터만' 표현하는 포맷으로 바꾼다
  • ② 불가피하면 허용 목록 기반 필터
    • ObjectInputFilter (Java 9+) / -Djdk.serialFilter
    • 차단 목록은 새 가젯이 나오면 뚫린다 — 반드시 허용 목록으로
  • ③ 필드에 transient 를 다는 것은 방어가 아니다 (정보 노출만 줄인다)

언어 차원의 결론

자바 직렬화는 설계상 결함으로 평가되어 '제거 대상' 으로 분류됐다

  • JEP 154 로 일부 기능이 이미 폐기 경로에 올랐다
  • record 는 직렬화 시 생성자를 거치므로 상대적으로 안전하다

Effective Java 3판의 결론도 같다 — "자바 직렬화를 새로 쓰지 마라"

실무에서 남아 있는 곳

세션 저장소, 분산 캐시, RMI, JMX, 일부 메시지 브로커 페이로드. 외부에 노출된 포트에서 자바 직렬화를 받고 있지 않은지 확인하는 것이 점검의 출발점이다.

JSON 도 안전한 것은 아니다

"자바 직렬화 대신 JSON 을 쓰면 된다" 는 절반만 맞다

Jackson 의 다형 역직렬화(@JsonTypeInfo · enableDefaultTyping) 를 켜면 JSON 안의 타입 정보를 보고 임의 클래스를 인스턴스화한다

  • 같은 가젯 문제가 그대로 재현된다 (CVE 가 여러 건 나왔다)

안전한 사용

  • 기본 타이핑을 켜지 않는다
  • 켜야 하면 PolymorphicTypeValidator 로 허용 타입을 못 박는다
  • 역직렬화 대상은 DTO 로 한정하고 엔티티를 직접 받지 않는다

점검 체크리스트

□ ObjectInputStream 을 외부 입력에 쓰는 곳이 있는가 □ 세션을 직렬화해 외부 저장소에 넣는가 (저장소가 뚫리면 RCE 로 이어진다) □ RMI · JMX 포트가 외부에 열려 있는가 □ 캐시(예: 분산 캐시) 직렬화 방식이 자바 직렬화인가 □ jdk.serialFilter 가 설정돼 있는가

가장 빠른 점검은 "역직렬화 지점이 어디인가" 를 먼저 목록화하는 것이다

면접 함정

  • "Serializable만 안 붙이면 안전" → 클래스패스의 다른 클래스가 가젯이 된다.
  • "차단 목록으로 충분" → 새 가젯이 계속 발견된다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 실무 노하우·실제 사고 사례