flawopen.com/안전하지 않은 역직렬화/PHP unserialize()
PHP unserialize()는 신뢰할 수 없는 입력에 안전한가요?
allowed_classes를 설정해도 신뢰할 수 없는 입력을 unserialize()에 전달하지 마세요. PHP는 객체를 복원하고 __unserialize() 또는 __wakeup()을 호출할 수 있으며, 공격자는 애플리케이션이나 오토로더에서 사용 가능한 클래스를 악용할 수 있습니다. 신뢰할 수 없는 데이터에는 JSON을 사용하세요. 연관 배열 디코딩을 사용하면 JSON 객체는 배열이 되어 매직 메서드 동작이 있는 애플리케이션 클래스를 인스턴스화하지 않습니다.
<?php // Cookie content is controlled by the client. $input = $_COOKIE['prefs'] ?? ''; $data = unserialize($input); // Restoring an object may invoke its // __unserialize() or __wakeup() method. // Later object destruction may invoke __destruct(). // The risk depends on available classes, // their serialized state, and reachable behavior.
<?php
$input = $_POST['prefs'] ?? null;
if (!is_string($input)) {
http_response_code(400);
exit;
}
try {
$data = json_decode(
$input,
true,
512,
JSON_THROW_ON_ERROR
);
} catch (JsonException $e) {
http_response_code(400);
exit;
}
if (!is_array($data)) {
http_response_code(400);
exit;
}
// Validate required keys and value types here.
PHP 객체 주입 체인이 성립하는 조건
객체 주입이 자동 코드 실행을 뜻하지는 않습니다. 직렬화 값은 PHP가 해석할 수 있고 옵션에서 허용한 클래스를 가리켜야 하며, 복원 또는 후속 사용이 보안 영향을 주는 동작에 도달해야 합니다. 객체 복원 중 PHP는 __unserialize() 또는 __wakeup()을 호출할 수 있습니다. 객체 파괴 시 __destruct(), 문자열 문맥에서 사용할 때만 __toString()이 실행될 수 있습니다.
Property-Oriented Programming (POP) 체인은 애플리케이션이나 로드된 종속성에 이미 있는 동작을 연결합니다. 악용 가능성은 실제 클래스, 직렬화된 속성, 호출 경로, 런타임 조건에 따라 달라집니다. 대상의 gadget 체인을 입증해야 하며 unserialize()가 있다는 사실만으로는 부족합니다.
문자열 필터링과 allowed_classes가 완전한 해결책이 아닌 이유
O: 같은 표시를 차단하거나 클래스 이름을 필터링하고 문자열 길이를 제한해도 PHP 직렬화를 안전하게 검증할 수 없습니다. 더 중요한 점은 PHP가 allowed_classes 값과 관계없이 신뢰할 수 없는 입력을 unserialize()에 전달하지 말라고 명시적으로 경고한다는 것입니다. 허용되지 않은 클래스는 __PHP_Incomplete_Class 객체가 되지만 구조는 계속 파싱됩니다. 신뢰할 수 있는 레거시 데이터는 결과 형태를 검증하고 깊이를 제한하세요. 클라이언트 상태에는 JSON을 사용하세요.
자주 묻는 질문
아니요. 정상적인 애플리케이션 클래스 복원을 막고 허용되지 않은 객체는 __PHP_Incomplete_Class가 됩니다. PHP는 이 옵션을 써도 신뢰할 수 없는 입력에 unserialize()를 사용하지 말라고 경고합니다. 신뢰된 레거시 데이터의 defense-in-depth로만 사용하고 결과를 검증하며 max_depth를 제한하세요. PHP 매뉴얼에 따르면 이 옵션은 열거형에 적용되지 않습니다.
키를 비밀로 유지하고 애플리케이션이 생성한 데이터의 서명을 역직렬화 전에 hash_equals()로 검증하면 무결성을 확인할 수 있습니다. 파싱 전에 잘못된 서명을 거부하세요. HMAC은 암호화가 아니며 재전송도 막지 않습니다. 최신성이 필요하면 만료 시간이나 nonce를 추가하세요. 임의의 사용자 직렬화 데이터를 신뢰 가능하게 만들기 위해 서명하지 마세요.
모두 직렬화 데이터에서 객체를 복원하는 광범위한 위험이 있지만 형식, 훅, 클래스 해석, 완화책은 다릅니다. 각 런타임과 사용 가능한 클래스를 따로 평가하고 한 플랫폼의 gadget 체인이 다른 곳에서도 동작한다고 가정하지 마세요.