FlawOpen
● CWE-502 / Unsafe Deserialization

안전하지 않은 역직렬화: 위험과 대응

신뢰할 수 없는 pickle, Java serialization, PHP unserialize 데이터가 위험한 동작을 유발하는 방식과 이를 대체하거나 제한하는 방법을 설명합니다.

💡 쉬운 설명 (ELI5)

직렬화는 모형 조립 설명서를 우편으로 보내는 것과 같습니다. 역직렬화는 그 설명을 따라 데이터를 다시 만듭니다. 일부 형식은 객체를 복원하는 중 클래스를 지정하거나 hook을 호출할 수도 있습니다. 신뢰할 수 없는 설명을 받아들이면 의도하지 않은 동작이 발생할 수 있으며, 영향은 형식, runtime, 사용 가능한 클래스, 애플리케이션 상황에 따라 달라집니다.

Core Concepts & Key Terms

직렬화 / 역직렬화
데이터나 객체를 저장·전송용 표현으로 바꾸고 프로그램에서 다시 분석하거나 복원하는 과정입니다.
역직렬화 hook
Python __reduce__, Java readObject, PHP __wakeup / __unserialize 같은 callback으로 객체 복원 중 실행될 수 있습니다.
Gadget chain
이미 포함된 라이브러리 동작을 연결해 의도하지 않은 효과를 내는 경로입니다. 가능성은 앱과 버전에 따라 다릅니다.
역직렬화 필터
객체 스트림을 읽을 때 클래스나 객체 그래프 지표를 제한하는 정책입니다. 위험을 줄이지만 신뢰할 수 없는 네이티브 객체 스트림을 권장 형식으로 만들지는 않습니다.

How Unsafe Deserialization Can Become an Exploit Chain

Step 1

신뢰할 수 없는 바이트가 decoder에 도달

Cookie, API body, queue message, 저장 데이터에 공격자가 영향을 줄 수 있는 값이 들어갑니다.

Step 2

앱이 네이티브 객체 형식을 호출

코드가 해당 바이트를 Python pickle, Java ObjectInputStream, PHP unserialize() 등에 전달합니다.

Step 3

복원 과정에서 형식별 동작이 발생

형식과 접근 가능한 클래스에 따라 constructor, hook, autoload, 라이브러리 메서드가 앱 검증 전에 실행될 수 있습니다.

Step 4

영향은 접근 가능한 동작에 좌우

사용 가능한 gadget이나 hook은 무단 효과를 낼 수 있지만 다른 입력은 실패하거나 자원만 소비할 수 있습니다. 항상 코드 실행으로 이어지는 것은 아닙니다.

Python / Flask: Unsafe Object Loading vs. Primitive JSON Validation

취약한 패턴
# VULNERABLE: pickle can execute behavior during unpickling.
from flask import Flask, request
import pickle

app = Flask(__name__)

@app.post("/api/session")
def load_session():
    # Never deserialize attacker-controlled bytes with pickle.
    session = pickle.loads(request.get_data())
    return {"user_id": session.user_id}
더 안전한 검증
# SAFER: parse JSON primitives, then validate expected shape.
from flask import Flask, abort, request

app = Flask(__name__)
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024

@app.post("/api/session")
def load_session():
    payload = request.get_json()
    if not isinstance(payload, dict):
        abort(400, "Expected a JSON object")

    user_id = payload.get("user_id")
    if type(user_id) is not int or user_id <= 0:
        abort(400, "Invalid user_id")

    # Resolve authorization and session state server-side.
    return {"user_id": user_id}

엔지니어링 보안 강화 체크리스트

JSON에도 입력 크기 제한과 구조 검증이 필요합니다. 레거시 객체 형식에서는 클래스 허용 목록만 경계로 삼지 말고, 역직렬화 전에 직렬화된 정확한 바이트의 진위를 확인하고 키를 보호하세요. MAC은 데이터를 암호화하지 않으며 재전송 공격도 막지 않습니다.

← 전체 보안 디렉터리 보기 취약점 가이드 보기 →