flawopen.com/안전하지 않은 역직렬화/Python pickle 보안
Python pickle.load()은 신뢰할 수 없는 데이터에 안전한가요?
아니요. 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:
- 신뢰할 수 없는 데이터의 역직렬화입니다. 공격자가 로드 데이터를 제공하거나 바꿀 수 있는지가 영향도를 좌우합니다.
신뢰할 수 없는 바이트가 pickle에 도달
업로드, 요청, 캐시 또는 산출물이 공격자 제어·변조 pickle 데이터를 프로세스에 넣을 수 있습니다.
Unpickler가 전역 객체를 찾음
인터프리터가 복원 명령을 처리하며 find_class()는 함수 등 이름 있는 전역 객체를 가져와 반환할 수 있습니다.
로드 중 동작이 실행
load() 또는 loads()가 실행되는 동안 반환 객체를 검증하기 전에 함수가 호출될 수 있습니다.
영향은 프로세스 권한에 좌우됨
동작은 Python 프로세스 권한으로 실행됩니다. 네트워크, 비밀 정보, 파일 접근과 격리가 피해 범위를 정합니다.
pickle 로드가 코드 실행 경계를 넘는 이유
pickle 스트림은 전역 객체와 복원 함수를 지정할 수 있습니다. Unpickling 중 Unpickler.find_class()가 함수 등의 전역 객체를 확인하고 opcode가 복원을 실행할 수 있습니다. 위험한 단계는 pickle.load() 또는 pickle.loads() 자체이며 별도의 후속 execute 호출은 없습니다. 코드는 프로세스 권한으로 실행됩니다. 이는 형식의 문서화된 동작이므로 신뢰할 수 없는 데이터 교환에 pickle을 쓰지 마세요.
JSON이 해결하는 것과 해결하지 않는 것
Python 문서는 신뢰할 수 없거나 변조된 pickle을 로드하면 임의 코드가 실행될 수 있다고 경고합니다. JSON 디코딩 자체는 그 코드 실행 경로를 만들지 않습니다. 하지만 JSON이 입력을 자동 검증하지는 않습니다. 크기, 최상위 형식, 필드, 스키마와 권한을 확인하세요. 기존 시스템에서 pickle이 꼭 필요하면 보호된 키로 정확한 바이트를 로드 전에 인증하세요. 서명은 키 가정이 맞을 때 출처와 무결성만 증명하며 신뢰된 서명자의 악성 데이터까지 안전하게 만들지는 않습니다. 데이터 전용 형식으로 이전하세요.
- ✓요청, 업로드, 공유 캐시 또는 출처 불명 산출물을 처리하는 경로에서
pickle.load()와pickle.loads()를 제거하세요. - ✓JSON 등 데이터 전용 형식을 사용하고 크기, 스키마, 허용 필드, 값 범위, 엔드포인트 권한을 검증하세요.
- ✓신뢰된 내부 흐름에서 pickle을 임시 유지한다면 정확한 바이트의 HMAC/서명을 먼저 확인하고 키와 프로세스 권한을 보호하세요.
- ✓코드와 데이터 흐름에서 pickle 사용을 찾아 각 출처를 누가 변경할 수 있는지 확인하고 위험한 사용을 이전하거나 격리하세요.
자주 묻는 질문
find_class()를 재정의해 전역 객체를 제한하면 위험을 줄일 수 있지만 모든 프로토콜과 도달 가능한 동작의 안전성을 입증하기는 어렵습니다. 사용자 바이트의 기본 방어로 허용 목록을 쓰지 말고 데이터 전용 형식을 선택하세요.
네. JSON은 디코딩 중 pickle식 코드 실행을 피하지만 형식, 필드, 크기, 값, 권한은 여전히 검증해야 합니다. JSON 파싱과 업무 규칙을 분리하세요.