FlawOpen

flawopen.com/Небезопасная десериализация/PHP unserialize()

Безопасен ли PHP unserialize() для недоверенных данных?

CWE-502: Deserialization of Untrusted DataСправочная страница
Краткий ответ

Не передавайте недоверенные данные в unserialize(), даже если задан allowed_classes. 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) связывает поведение, уже присутствующее в приложении или загруженных зависимостях. Эксплуатируемость зависит от конкретных классов, сериализованных свойств, пути вызовов и условий выполнения. Цепочку гаджетов для цели нужно подтвердить; одного unserialize() недостаточно.

Почему фильтрация строки и allowed_classes не являются полной защитой

Блокировка маркера O:, фильтрация имён классов или ограничение длины не обеспечивают безопасную проверку PHP-сериализации. PHP прямо предупреждает не передавать недоверенный ввод в unserialize() независимо от allowed_classes. Запрещённые классы превращаются в __PHP_Incomplete_Class, но структура всё равно разбирается. Для доверенных старых данных проверяйте форму результата и ограничивайте глубину. Для состояния клиента используйте JSON.

Частые вопросы

Делает ли allowed_classes => false недоверенные данные безопасными?

Нет. Параметр запрещает обычное восстановление классов приложения; запрещённые объекты становятся __PHP_Incomplete_Class. PHP всё равно предостерегает от недоверенных данных для unserialize(), даже с этой опцией. Используйте её лишь как дополнительную защиту доверенных старых данных, проверяйте результат и ограничивайте max_depth. В руководстве PHP указано, что эта опция не влияет на перечисления.

Делает ли HMAC сериализованные данные безопасными?

Он может подтвердить целостность данных, созданных приложением, если ключ секретен и hash_equals() проверяет подпись до десериализации. Отклоняйте неверную подпись до разбора. HMAC не шифрует данные и не предотвращает повтор; при необходимости добавьте срок действия или nonce. Не подписывайте произвольные пользовательские данные, чтобы объявить их доверенными.

Это тот же риск, что у Python pickle или Java ObjectInputStream?

У них есть общий риск восстановления объектов из сериализованных данных, но форматы, хуки, разрешение классов и меры защиты различаются. Оценивайте каждый runtime и его классы отдельно; цепочка гаджетов для одной платформы не обязана работать в другой.

Источники

См. также Небезопасная десериализация: риски и защита | flawopen.com Python pickle.load(): риск недоверенных данных Безопасен ли Ruby YAML.load? Версии Psych и safe_load .NET BinaryFormatter: риски и переход Безопасность ObjectInputStream в Java: защита десериализации Newtonsoft.Json TypeNameHandling: безопасная настройка?