PHP 过滤 unserialize()(建议收藏)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

在 PHP 开发中,unserialize() 函数是一个功能强大的工具,但它也是一把双刃剑。当开发者使用该函数处理不可信数据时,可能会引入严重的安全风险,例如远程代码执行(RCE)或对象注入攻击。本文将从基础概念出发,结合实际案例,深入探讨如何通过合理的过滤策略保护 PHP 应用程序免受 unserialize() 相关攻击。无论是编程新手还是有一定经验的开发者,都能从中获得实用的安全防护知识。


2.1 序列化与反序列化的基础

什么是序列化?

序列化(Serialization)是将数据结构或对象转换为可存储或传输的格式(如字符串或二进制流)的过程。在 PHP 中,serialize() 函数可以将变量转换为字符串,例如:

$user = [  
    "name" => "Alice",  
    "age" => 30  
];  
$serialized = serialize($user);  
echo $serialized; // 输出:"a:2:{s:4:"name";s:5:"Alice";s:3:"age";i:30;}"  

反序列化的潜在风险

反序列化(Unserialization)则是序列化的逆过程,通过 unserialize() 将序列化后的字符串重新转换为 PHP 变量。然而,如果反序列化的数据来自不可信来源(如用户输入或外部接口),攻击者可能构造恶意序列化字符串,触发以下风险:

  • 对象注入:攻击者伪造一个包含恶意方法的对象,触发其 __wakeup()__destruct() 等魔术方法。
  • 数据篡改:修改序列化字符串中的值,绕过应用层校验逻辑。

形象比喻

想象 unserialize() 是一个快递员,而序列化字符串是包裹。如果快递员盲目打开任何包裹,攻击者可能在包裹中放置炸弹(恶意代码)。因此,严格检查包裹内容(过滤数据)是必要的。


2.2 unserialize() 的安全风险详解

案例:对象注入攻击

假设有一个简单的用户登录系统,代码如下:

// 危险代码示例  
if (isset($_POST['data'])) {  
    $user = unserialize($_POST['data']); // 直接反序列化用户输入  
    echo "Welcome back, " . $user->username;  
}  

攻击者可以构造一个恶意对象:

// 攻击者构造的序列化字符串  
$payload = 'O:8:"stdClass":1:{s:8:"username";s:5:"admin";}';  

当此字符串被 unserialize() 处理时,会生成一个 stdClass 对象,覆盖原有验证逻辑,直接赋予用户管理员权限。

更严重的漏洞:远程代码执行

某些 PHP 版本或框架中,攻击者可能利用特定类(如 SimpleXMLIterator)的 __toString() 方法执行任意代码。例如:

// 漏洞利用示例(假设存在危险类)  
$payload = 'O:20:"SimpleXMLIterator":1:{s:16:"_XML Strauss";s:10:"php://input";}'  
// 反序列化时可能触发系统命令执行  

2.3 PHP 过滤 unserialize() 的核心策略

策略一:严格验证数据来源

原则永不直接反序列化不可信数据。在反序列化前,必须通过以下方式过滤:

  1. 白名单机制:仅允许特定类或数据结构被反序列化。
  2. 类型检查:确保序列化字符串符合预期格式。

白名单过滤示例

// 白名单过滤函数  
function safe_unserialize($data) {  
    // 白名单中的允许类  
    $allowed_classes = ['User', 'Product'];  
    // 使用自定义反序列化处理程序  
    ini_set('unserialize_callback_func', 'class_loader');  
    return unserialize($data);  
}  

function class_loader($class) {  
    if (!in_array($class, ['User', 'Product'])) {  
        trigger_error("禁止反序列化类 $class", E_USER_WARNING);  
        return false;  
    }  
    return new $class();  
}  

策略二:避免敏感类的使用

某些 PHP 内置类(如 SimpleXMLIteratorPharData)存在高风险,应尽量避免将其用于序列化操作。

策略三:使用替代方案

推荐方案

  1. JSON 格式:使用 json_encode()json_decode() 替代序列化。
  2. 框架工具:Laravel 等框架提供了更安全的序列化工具,例如 Crypt 类。
// 安全的 JSON 替代方案  
$data = $_POST['data'];  
if (filter_var($data, FILTER_VALIDATE_JSON)) {  
    $user = json_decode($data);  
}  

2.4 实际案例与防御实践

案例场景:用户配置存储

假设需要存储用户偏好设置:

// 危险实现(直接序列化)  
setcookie('user_prefs', serialize($_POST['prefs']));  
// 随后在其他页面:  
$prefs = unserialize($_COOKIE['user_prefs']);  

攻击者可能的利用方式

  1. 修改 Cookie:将 user_prefs 的值替换为恶意序列化字符串,例如:
    O:8:"stdClass":1:{s:8:"username";s:5:"admin";}  
    
  2. 绕过权限检查:直接获取管理员权限。

安全修复方案

// 安全实现:白名单 + 类型检查  
// 存储时  
$allowed_keys = ['theme', 'language'];  
$prefs = array_intersect_key($_POST['prefs'], array_flip($allowed_keys));  
setcookie('user_prefs', base64_encode(json_encode($prefs)));  

// 读取时  
if (isset($_COOKIE['user_prefs'])) {  
    $data = json_decode(base64_decode($_COOKIE['user_prefs']), true);  
    if (json_last_error() === JSON_ERROR_NONE) {  
        // 安全使用 $data  
    }  
}  

2.5 最佳实践与工具推荐

代码级防御

  1. 禁用危险函数:在 php.ini 中设置 disable_functions = unserialize(适用于完全不需要反序列化的场景)。
  2. 使用过滤器:结合 filter_var() 对输入进行格式验证。

开发流程建议

  • 静态代码分析:使用工具如 RIPS 或 PHPStan 检测 unserialize() 的潜在风险。
  • 依赖管理:通过 composer 确保第三方库无已知反序列化漏洞。

结论

PHP 的 unserialize() 函数若使用不当,可能成为攻击者入侵系统的突破口。通过严格的输入过滤、白名单机制、替代方案选择以及安全编码实践,开发者可以有效降低此类风险。记住:数据验证和输入过滤是安全开发的基石。希望本文能帮助读者建立对 PHP 过滤 unserialize() 的全面理解,并在实际项目中践行安全开发原则。

关键词布局:PHP 过滤 unserialize()、反序列化安全、对象注入防御、白名单机制、JSON 替代方案

最新发布