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) 链会串联应用或已加载依赖中已有的行为。可利用性取决于实际类、序列化属性、调用路径和运行条件。必须针对目标证明具体 gadget 链;仅有 unserialize() 并不能证明可利用。

为什么字符串过滤和 allowed_classes 不是完整修复

阻止 O: 等标记、过滤类名或限制字符串长度,都不能安全验证 PHP 序列化。更重要的是,PHP 明确警告:无论 allowed_classes 如何设置,都不要将不可信输入传给 unserialize()。不允许的类会变为 __PHP_Incomplete_Class,但结构仍会被解析。处理可信旧数据时,应验证结果形状并限制深度;客户端状态应使用 JSON。

常见问题

allowed_classes => false 能让不可信输入安全吗?

不能。它会阻止正常恢复应用类;不允许的对象会变成 __PHP_Incomplete_Class。PHP 仍警告不要将不可信输入传给 unserialize(),即使设置了此选项。仅将其作为可信旧数据的纵深防御,并验证结果、限制 max_depth。 PHP 手册还说明,该选项不影响枚举。

HMAC 能让序列化数据安全吗?

如果密钥保密,且在反序列化前用 hash_equals() 验证由应用生成的数据签名,HMAC 可以确认完整性。解析前必须拒绝无效签名。HMAC 不加密,也不能防止重放;需要时应加入过期时间或 nonce。不要用签名把任意用户序列化数据变成可信数据。

这与 Python pickle 或 Java ObjectInputStream 风险相同吗?

它们都存在从序列化数据恢复对象的广义风险,但格式、钩子、类解析规则和缓解措施各不相同。应分别评估每种运行时及可用类;一种平台的 gadget 链不一定适用于另一种。

参考资料

另请参阅 不安全反序列化:风险与修复 | flawopen.com Python pickle.load():不可信数据的安全风险 Ruby YAML.load 安全吗?Psych 版本与 safe_load .NET BinaryFormatter:风险与迁移 Java ObjectInputStream 安全:降低反序列化风险 Newtonsoft.Json TypeNameHandling:安全配置?