티스토리 뷰
728x90
반응형
자바 직렬화의 대안을 찾으라
💡직렬화(Serialization)란?
- 객체를 직렬화하여 전송 가능한 형태로 만드는 것을 의미합니다.
- 자바의 I/O 처리는 정수, 문자열, 바이트 단위의 처리만 지원하기 때문에 복잡한 객체 또는 데이터를 외부 자바 시스템에서도 사용할 수 있도록 바이트 형태로 데이터를 변환하는 기술을 직렬화라고 부릅니다.
- 시스템적으로는 JVM 메모리에 상주하고 있는(힙 또는 스택) 객체 데이터를 바이트 형태로 변환하는 기술입니다.
- 직렬화에서는 java.io.ObjectOutputStream 패키지가 사용되며, 스트림에 객체를 출력하는 역할을 하며 ObjectOutputStream 객체의 writeObject 메서드는 객체를 직렬화한 후에 스트림으로 보내는 기능을 합니다.
💡역직렬화(Deserialization)란?
- 직렬화된 객체 또는 데이터를 역으로 직렬화하여 다시 객체 형태로 만드는 것을 의미합니다. 저장된 파일을 읽거나 전송된 스트림 데이터를 읽어 원래 객체의 형태로 복원합니다.
- 역직렬화에서는 java.io.ObjectInputStream 패키지가 사용되며, 스트림으로부터 객체를 입력하는 역할을 수행하며 ObjectInputStream 객체의 readObject 메서드는 스트림에서 객체를 역직렬화하여 데이터를 읽어오고 필드를 미리 세팅한 객체의 변수에 저장하는 기능을 말합니다.
💡자바에서 직렬화가 위험한 이유
- 자바에서 ObjectInputStream의 readObject 메서드를 통해 객체의 그래프가 역직렬화되는데 문제는 바이트 스트림을 역직렬화 하는 과정에서 그 객체 안에 있는 모든 코드를 수행할 수 있는데, 이는 코드 전체가 공격 범위에 들어간다는 것입니다. 모든 직렬화 가능 클래스들을 공격에 대비하더라도 애플리케이션을 취약하게 만들 수 있습니다.
💡직렬화 역직렬화 예제 코드
@AllArgsConstructor
@ToString
public class Member implements Serializable {
private String name;
private int age;
}
public class Main {
public static final String FILE_PATH = "src/member.txt";
public static void main(String[] args) {
try {
Member member = new Member("홀길동", 30);
doSerializable(member);
Member serializationMember = doUnSerializable();
System.out.println(serializationMember); // Member(name=홀길동, age=30)
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
}
// 직렬화
private static void doSerializable(Member member) throws IOException {
FileOutputStream fos = new FileOutputStream(FILE_PATH);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(member);
oos.close();
}
// 역직렬화
private static Member doUnSerializable() throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream(FILE_PATH);
ObjectInputStream ois = new ObjectInputStream(fis);
Member member = (Member) ois.readObject();
ois.close();
return member;
}
}
💡직렬화를 사용하지 말자
- 신뢰할 수 없는 바이트 스트림을 역직렬화하는 작업은 스스로를 공격에 노출시키는 행위로 처음부터 직렬화, 역직렬화를 사용하지 않는게 가장 안전합니다. 객체와 바이트 시퀀스를 변환해주는 다른 기술들이 많이 있는데, 직렬화 시스템 혹은 크로스-플랫폼 구조화된 데이터 표현이라고 합니다.
- 위와 같은 방식은 임의 객체 그래프를 직렬화/역직렬화하는 대신 기본 타입 몇개와 배열 타입만 지원합니다. 이정도의 추상화로도 충분히 직렬화의 문제점을 회피할 수 있습니다. 이런 데이터의 표현으로는 JSON과 프로토콜 버퍼가 있습니다.
📜 JSON
- 더클라스 크록퍼드(Douglas Crockford)가 브라우저와 서버의 통신용으로 설계
- 자바 스크립트용으로 만들어졌습니다.
- 텍스트 기반이라 사람이 읽을 수 있습니다.
- 오직 데이터를 표현하기 위해 사용됩니다.
📜 프로토콜 버퍼
- 구글이 서버 사이에 데이터를 교환하고 저장하기 위한 목적으로 설계
- C++용으로 만들어졌습니다.
- 이진 표현이라 효율이 높습니다.
- 문서를 위한 스키마를 제공하고 올바로 쓰도록 강요합니다.
💡신뢰할 수 없는 데이터는 절대 역직렬화하지 말라
- 레거시 시스템 때문에 자바 직렬화를 완전히 배제할 수 없을 때의 차선책으로는 신뢰할 수 없는 데이터는 절대 역직렬화하지 않는 것입니다. 자바의 공식 보안 코딩 지침에서는 "신뢰할 수 없는 데이터의 역직렬화는 본질적으로 위험하므로 절대로 피해야 한다" 라고 조언하고 있습니다.
- 하지만 직렬화를 피할 수 없고 역직렬화한 데이터가 안전한지 완전히 확신할 수 없는 경우 객체 역직렬화 필터링을 사용할 수 있습니다.
💡객체 역직렬화 필터링(java.io.ObjectInputFilter)
- 자바 9에 추가되었으며, 이전 버전에서도 사용할 수 있도록 이식되었습니다.
- 객체 역직렬화 필터링은 데이터 스트림이 역직렬화되기 전에 필터를 설치하는 기능입니다.
- 클래스 단위로 특정 클래스를 받아들이거나 거부할 수 있습니다.
- 기본 수용 모드 : 블랙리스트에 기록된 잠재적으로 위험한 클래스들을 거부합니다.
- 기본 거부 모드 : 화이트리스트에 기록된 안전하다고 알려진 클래스들만 수용합니다.
- 블랙리스트 방식보다는 화이트리스트 방식을 추천하고 있습니다. 그 이유눈 블랙리스트 방식은 이미 알려진 위험으로부터만 보호할 수 있기 때문입니다.
- 필터링 기능은 메모리를 과하게 사용하거나 객체 그래프가 너무 깊어지는 사태로부터 보호해줍니다. 하지만 직렬화 폭탄은 걸러내지 못합니다.(deserialzation bomb)
728x90
반응형
'스터디 > 이펙티브 자바' 카테고리의 다른 글
이펙티브 자바 - Item88. readObject 메서드는 방어적으로 작성하라 (0) | 2022.08.27 |
---|---|
이펙티브 자바 - Item86. Serialization을 구현할지는 신중히 결정하라 (0) | 2022.08.27 |
이펙티브 자바 - Item83. 지연 초기화는 신중히 사용하라 (0) | 2022.08.25 |
이펙티브 자바 - Item82. 스레드 안전성 수준을 문서화하라 (0) | 2022.08.24 |
이펙티브 자바 - Item81. wait와 notify보다는 동시성 유틸리티를 애용하라 (0) | 2022.08.23 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- java ThreadLocal
- spring boot poi excel download
- space based architecture
- spring boot redisson sorted set
- spring boot excel download paging
- spring boot redis 대기열 구현
- transactional outbox pattern
- redis sorted set으로 대기열 구현
- 공간 기반 아키텍처
- pipe and filter architecture
- polling publisher spring boot
- redis sorted set
- 레이어드 아키텍처란
- spring boot redisson destributed lock
- 트랜잭셔널 아웃박스 패턴 스프링부트
- pipeline architecture
- 람다 표현식
- transactional outbox pattern spring boot
- java userThread와 DaemonThread
- JDK Dynamic Proxy와 CGLIB의 차이
- 트랜잭셔널 아웃박스 패턴 스프링 부트 예제
- service based architecture
- spring boot excel download oom
- microkernel architecture
- spring boot redisson 분산락 구현
- 자바 백엔드 개발자 추천 도서
- spring boot 엑셀 다운로드
- redis 대기열 구현
- @ControllerAdvice
- 서비스 기반 아키텍처
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
글 보관함