flawopen.com/不安全的反序列化/PHP unserialize()
PHP unserialize() 能安全处理不可信输入吗?
不要将不可信输入传给 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。
常见问题
不能。它会阻止正常恢复应用类;不允许的对象会变成 __PHP_Incomplete_Class。PHP 仍警告不要将不可信输入传给 unserialize(),即使设置了此选项。仅将其作为可信旧数据的纵深防御,并验证结果、限制 max_depth。 PHP 手册还说明,该选项不影响枚举。
如果密钥保密,且在反序列化前用 hash_equals() 验证由应用生成的数据签名,HMAC 可以确认完整性。解析前必须拒绝无效签名。HMAC 不加密,也不能防止重放;需要时应加入过期时间或 nonce。不要用签名把任意用户序列化数据变成可信数据。
它们都存在从序列化数据恢复对象的广义风险,但格式、钩子、类解析规则和缓解措施各不相同。应分别评估每种运行时及可用类;一种平台的 gadget 链不一定适用于另一种。