본문 바로가기
Security보안

직렬화(Serialization)와 역직렬화(Deserialization): 데이터전송, 저장시 필요성과 보안 취약점 및 해결 방법

by ICT 인사이트 2025. 3. 13.
728x90

 

오늘날 소프트웨어 개발 환경에서는 데이터를 저장, 전송, 처리하는 과정이 필수적입니다.
우리는 웹 애플리케이션, 데이터베이스, 네트워크 프로토콜, 파일 시스템 등에서 데이터를 효율적으로 다루기 위해 다양한 기술을 사용합니다.

이 과정에서 "객체(Object) 데이터를 효율적으로 저장하고, 필요할 때 다시 불러올 수 있다면 어떨까요?"
또한, "네트워크를 통해 객체(Object)를 전송할 때 이를 안전하게 주고받을 수 있는 방법은 무엇일까요?"

 

이 문제를 해결하는 것이 바로 직렬화(Serialization)와 역직렬화(Deserialization) 입니다.

하지만, 여기서 주의해야 할 중요한 점이 있습니다.
잘못된 역직렬화는 보안 취약점을 발생시켜 공격자가 이를 악용할 수도 있습니다.
실제로 많은 보안 사고가 역직렬화 취약점을 통해 발생하며, 보안에 대한 인식이 부족한 개발자들에게 치명적인 결과를 초래할 수 있습니다.

 

그렇다면,
✅ 직렬화와 역직렬화는 정확히 무엇일까요?
✅ 어떻게 사용하며, 어떤 문제점이 존재할까요?
✅ 보안 취약점을 피하고 안전하게 데이터를 관리하는 방법은 무엇일까요?

 

이번 포스팅에서는 직렬화와 역직렬화의 개념, 실무 활용, 보안 취약점 및 안전한 코드 작성 방법을 구체적으로 설명하겠습니다. 개발자들이 보다 안전한 시스템을 구축하고, 직렬화된 데이터를 효과적으로 활용할 수 있도록 깊이 있는 내용을 준비했으니 끝까지 읽어주세요! 🚀


1. 직렬화와 역직렬화란?

1.1 직렬화(Serialization) : 객체 데이터를 바이트 스트림(byte stream)으로 변환하여 저장하거나 전송할 수 있도록 하는 과정.
1.2 역직렬화(Deserialization) : 직렬화된 바이트 스트림을 다시 객체로 복원하는 과정. 잘못된 역직렬화 처리 시 보안 취약점 발생 가능.


2. 직렬화 및 역직렬화 코드 예제

2.1 Java에서 직렬화 및 역직렬화 예제

import java.io.*;

class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

public class SerializationExample {
    public static void main(String[] args) {
        Person person = new Person("Alice", 25);

        // 직렬화
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            oos.writeObject(person);
            System.out.println("직렬화 완료!");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 역직렬화
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person deserializedPerson = (Person) ois.readObject();
            System.out.println("역직렬화 완료! 이름: " + deserializedPerson.name);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
  • Serializable 인터페이스를 구현하면 해당 객체를 직렬화할 수 있음
  • ObjectOutputStream을 사용하여 객체를 파일(.ser)로 저장
  • ObjectInputStream을 사용하여 저장된 데이터를 객체로 복원(역직렬화)

2.2 Python에서 직렬화 및 역직렬화 예제

import pickle

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 직렬화
person = Person("Bob", 30)
with open("person.pkl", "wb") as f:
    pickle.dump(person, f)
print("직렬화 완료!")

# 역직렬화
with open("person.pkl", "rb") as f:
    loaded_person = pickle.load(f)
print(f"역직렬화 완료! 이름: {loaded_person.name}, 나이: {loaded_person.age}")

설명

  • pickle 모듈을 사용하여 객체를 파일로 저장(직렬화)
  • pickle.load()를 사용하여 다시 객체로 변환(역직렬화)

3. 직렬화 및 역직렬화 보안 취약점 및 해결 방법

3.1 보안 취약점 (Insecure Deserialization)

  • 역직렬화 시 공격자가 악의적인 객체를 삽입하여 원격 코드 실행(RCE, Remote Code Execution)이 가능
  • 공격자는 조작된 바이트 스트림을 서버로 전송하여 임의의 코드 실행, 시스템 접근 권한 탈취, 데이터 변조 등의 공격을 수행

3.2 보안 취약점 예제 (Java 역직렬화 취약점 - 공격 코드 포함)

import java.io.*;

class Exploit implements Serializable {
    private static final long serialVersionUID = 1L;
    static {
        try {
            Runtime.getRuntime().exec("calc.exe"); // Windows 계산기 실행 (공격 가능)
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class InsecureDeserialization {
    public static void main(String[] args) {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("exploit.ser"))) {
            oos.writeObject(new Exploit());
            System.out.println("악성 객체 직렬화 완료!");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 역직렬화 실행 (취약 코드 실행됨)
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("exploit.ser"))) {
            ois.readObject(); 
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

문제점:

  • 공격자가 calc.exe 대신 시스템 명령어를 실행하는 코드를 삽입하면 서버 해킹 가능
  • Java의 역직렬화 취약점은 Log4j, Apache Commons-Collections 등의 라이브러리에서도 발생

3.3 안전한 직렬화 및 역직렬화 방법

1️⃣ 신뢰할 수 없는 데이터 역직렬화 방지

  • JSON, XML과 같은 안전한 데이터 포맷 사용
  • readObject() 사용 시 허용된 클래스만 역직렬화

2️⃣ 역직렬화 시 객체 필터링 적용 (Java 9 이상 지원)

import java.io.*;

public class SafeDeserialization {
    public static void main(String[] args) {
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("safe.ser"))) {
            Object obj = ois.readObject();
            if (obj instanceof Person) { 
                Person person = (Person) obj;
                System.out.println("안전한 역직렬화 완료: " + person.name);
            } else {
                throw new SecurityException("허용되지 않은 객체 역직렬화 차단!");
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

 

 

3️⃣ Python에서 안전한 직렬화 방법

  • pickle 대신 json 모듈 사용 (pickle은 임의 코드 실행 가능)
import json

data = {"name": "Alice", "age": 25}

# 안전한 직렬화
json_data = json.dumps(data)
print(f"직렬화된 데이터: {json_data}")

# 안전한 역직렬화
loaded_data = json.loads(json_data)
print(f"역직렬화된 데이터: {loaded_data}")

4. 🎯 결론

  • 직렬화(Serialization): 객체 데이터를 바이트 스트림으로 변환하여 저장 및 전송 가능
  • 역직렬화(Deserialization): 바이트 스트림을 객체로 변환하는 과정
  • 보안 취약점: 잘못된 역직렬화는 해커에게 공격 기회를 제공 (원격 코드 실행, 데이터 변조)
  • 안전한 사용법: 허용된 클래스만 역직렬화, JSON/XML 사용, 신뢰할 수 없는 데이터 차단

직렬화와 역직렬화는 강력한 기능이지만, 보안 취약점을 반드시 고려해야 합니다! 🚀

728x90