안전하지 않은 역직렬화: 위험과 대응
신뢰할 수 없는 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
신뢰할 수 없는 바이트가 decoder에 도달
Cookie, API body, queue message, 저장 데이터에 공격자가 영향을 줄 수 있는 값이 들어갑니다.
앱이 네이티브 객체 형식을 호출
코드가 해당 바이트를 Python pickle, Java ObjectInputStream, PHP unserialize() 등에 전달합니다.
복원 과정에서 형식별 동작이 발생
형식과 접근 가능한 클래스에 따라 constructor, hook, autoload, 라이브러리 메서드가 앱 검증 전에 실행될 수 있습니다.
영향은 접근 가능한 동작에 좌우
사용 가능한 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은 데이터를 암호화하지 않으며 재전송 공격도 막지 않습니다.
- Python pickle이나 PHP unserialize로 신뢰할 수 없는 네이티브 객체 스트림을 역직렬화하지 말고 schema와 크기를 검증한 JSON 등 primitive 형식을 사용하세요.
- Java serialization을 아직 제거할 수 없다면 ObjectInputFilter allowlist와 객체 그래프 한도를 설정하세요. 필터는 모든 배포에서 기본 활성화되지 않습니다.
- 기존 payload의 무결성이 필요하면 보호된 MAC 키로 역직렬화 전에 정확한 바이트를 인증하세요. MAC은 암호화나 replay 방지가 아닙니다.
- 세션 권한은 서버에 두거나 만료, audience, 키 교체를 지원하는 유지 관리되는 프레임워크 서명 토큰을 사용하세요.