FlawOpen

flawopen.com/안전하지 않은 역직렬화/Python pickle 보안

Python pickle.load()은 신뢰할 수 없는 데이터에 안전한가요?

CWE-502: 신뢰할 수 없는 데이터의 역직렬화참고 페이지
짧은 답변

아니요. pickle은 수동적인 데이터 기록이 아니라 소포 안의 프로그램처럼 다루세요. 로드 과정에서 앱이 객체를 검증하기 전에 복원 동작이 실행될 수 있습니다. 신뢰 경계를 넘는 입력에는 데이터 전용 형식을 사용하세요.

위험 — 신뢰할 수 없는 바이트가 로드 중 코드를 실행할 수 있음
import pickle

# Never accept pickle bytes from a request, upload,
# shared cache, or other untrusted source.
obj = pickle.loads(untrusted_bytes)
더 안전한 기본값 — 데이터를 파싱하고 스키마를 검증
import json

def parse_profile(raw: bytes) -> dict[str, str]:
    if len(raw) > 65_536:
        raise ValueError("Payload too large")

    try:
        value = json.loads(raw)
    except (json.JSONDecodeError, UnicodeDecodeError):
        raise ValueError("Invalid JSON") from None

    if not isinstance(value, dict) or set(value) - {"display_name", "theme"}:
        raise ValueError("Invalid profile")

    name = value.get("display_name")
    theme = value.get("theme")
    if not isinstance(name, str) or len(name) > 80:
        raise ValueError("Invalid display name")
    if theme not in {"light", "dark"}:
        raise ValueError("Invalid theme")

    return {"display_name": name, "theme": theme}
Pickle 스트림:
객체 복원 방식을 나타내는 Python 전용 바이트열로, 전역 참조와 복원 동작을 포함합니다.
Unpickling:
스트림을 해석해 객체 그래프를 복원하는 과정이며 함수가 호출될 수 있습니다.
신뢰 경계:
사용자, 서비스, 업로드 또는 공유 저장소가 바이트를 제어하거나 바꿀 수 있는 지점입니다.
CWE-502:
신뢰할 수 없는 데이터의 역직렬화입니다. 공격자가 로드 데이터를 제공하거나 바꿀 수 있는지가 영향도를 좌우합니다.
1단계

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

업로드, 요청, 캐시 또는 산출물이 공격자 제어·변조 pickle 데이터를 프로세스에 넣을 수 있습니다.

2단계

Unpickler가 전역 객체를 찾음

인터프리터가 복원 명령을 처리하며 find_class()는 함수 등 이름 있는 전역 객체를 가져와 반환할 수 있습니다.

3단계

로드 중 동작이 실행

load() 또는 loads()가 실행되는 동안 반환 객체를 검증하기 전에 함수가 호출될 수 있습니다.

4단계

영향은 프로세스 권한에 좌우됨

동작은 Python 프로세스 권한으로 실행됩니다. 네트워크, 비밀 정보, 파일 접근과 격리가 피해 범위를 정합니다.

pickle 로드가 코드 실행 경계를 넘는 이유

pickle 스트림은 전역 객체와 복원 함수를 지정할 수 있습니다. Unpickling 중 Unpickler.find_class()가 함수 등의 전역 객체를 확인하고 opcode가 복원을 실행할 수 있습니다. 위험한 단계는 pickle.load() 또는 pickle.loads() 자체이며 별도의 후속 execute 호출은 없습니다. 코드는 프로세스 권한으로 실행됩니다. 이는 형식의 문서화된 동작이므로 신뢰할 수 없는 데이터 교환에 pickle을 쓰지 마세요.

JSON이 해결하는 것과 해결하지 않는 것

Python 문서는 신뢰할 수 없거나 변조된 pickle을 로드하면 임의 코드가 실행될 수 있다고 경고합니다. JSON 디코딩 자체는 그 코드 실행 경로를 만들지 않습니다. 하지만 JSON이 입력을 자동 검증하지는 않습니다. 크기, 최상위 형식, 필드, 스키마와 권한을 확인하세요. 기존 시스템에서 pickle이 꼭 필요하면 보호된 키로 정확한 바이트를 로드 전에 인증하세요. 서명은 키 가정이 맞을 때 출처와 무결성만 증명하며 신뢰된 서명자의 악성 데이터까지 안전하게 만들지는 않습니다. 데이터 전용 형식으로 이전하세요.

자주 묻는 질문

제한된 Unpickler면 공격자가 만든 pickle도 안전한가요?

find_class()를 재정의해 전역 객체를 제한하면 위험을 줄일 수 있지만 모든 프로토콜과 도달 가능한 동작의 안전성을 입증하기는 어렵습니다. 사용자 바이트의 기본 방어로 허용 목록을 쓰지 말고 데이터 전용 형식을 선택하세요.

JSON도 검증해야 하나요?

네. JSON은 디코딩 중 pickle식 코드 실행을 피하지만 형식, 필드, 크기, 값, 권한은 여전히 검증해야 합니다. JSON 파싱과 업무 규칙을 분리하세요.

주요 참고 자료

함께 보기 안전하지 않은 역직렬화: 위험과 대응 | flawopen.com Ruby YAML.load는 안전한가요? Psych 버전과 safe_load .NET BinaryFormatter: 위험과 마이그레이션 Java ObjectInputStream 보안과 안전한 역직렬화 Newtonsoft.Json TypeNameHandling: 안전한 설정 PHP unserialize()와 신뢰할 수 없는 입력