首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP serialize() 與 unserialize() 深度解析:序列化機制、對象持久化、安全風險與最佳實踐

     編輯:PHP 相關知識

PHP serialize() 與 unserialize() 深度解析:序列化機制、對象持久化、安全風險與最佳實踐

serialize() 將 PHP 變量 (標量、陣列、對象) 轉為可存儲/傳輸的字串;unserialize() 還原。本文詳解機制、魔術方法、對象序列化細節、安全漏洞與現代替代方案。

基本語法與類型支援

<?php
// 支援類型:所有標量、陣列、對象
// 不支援:resource (資源句柄)

$arr = ['key' => 'website', 'value' => 'www.example.com'];
$str = serialize($arr);
// 輸出: a:2:{s:3:"key";s:7:"website";s:5:"value";s:17:"www.example.com";}

$restored = unserialize($str);
// $restored === $arr (值相等,但非同一引用)
?>

對象序列化:魔術方法與細節

<?php
class Dog {
    public $name;
    public $age;
    public $owner;
    
    public function __construct($name = 'unnamed', $age = 0, $owner = 'unknown') {
        $this->name  = $name;
        $this->age   = $age;
        $this->owner = $owner;
    }
    
    public function getAgeInDays() {
        return $this->age * 365;
    }
    
    // 序列化前調用:可清理資源、關閉連接、決定保存哪些屬性
    public function __sleep() {
        // 返回需序列化的屬性名陣列
        return ['name', 'age', 'owner'];  // 排除敏感/不可序列化屬性
    }
    
    // 反序列化後調用:重建連接、初始化狀態
    public function __wakeup() {
        // $this->db = new PDO(...);
    }
}

// 使用
$dog = new Dog('Rover', 12, 'Lisa and Graham');
$serialized = serialize($dog);
// O:3:"Dog":3:{s:4:"name";s:5:"Rover";s:3:"age";i:12;s:5:"owner";s:15:"Lisa and Graham";}

unset($dog);  // 銷毀原對象
$pet = unserialize($serialized);  // 還原,自動調用 __wakeup()

echo $pet->getAgeInDays();  // 4380
?>

版本差異關鍵點

版本對象序列化行為
PHP 3僅序列化屬性,方法丟失
PHP 4+屬性+方法均保留 (類定義必須存在)
PHP 7+增加 __serialize()/__unserialize() (PHP 7.4) 更精細控制

實戰場景:Session 存儲對象、緩存、隊列任務

<?php
// 1. Session 存儲用戶對象
$_SESSION['user'] = serialize($userObj);
// 取出
$userObj = unserialize($_SESSION['user']);

// 2. Redis 緩存複雜數據
$redis->setex('user:1001', 3600, serialize($userProfile));

// 3. 隊列任務傳遞對象
$payload = serialize(new SendEmailJob($to, $subject, $body));
$queue->push($payload);
// Worker 端
$job = unserialize($payload);
$job->handle();
?>

安全風險:反序列化漏洞 (Object Injection)

核心危害:攻擊者控制輸入字串,構造惡意對象,利用 `__wakeup()`、`__destruct()`、魔術方法鏈 (POP Chain) 觸發 RCE、文件讀寫、SQL注入等。

經典攻擊向量

<?php
// 易受攻擊代碼
$data = $_COOKIE['user_data'];
$user = unserialize($data);  // 直接反序列化用戶輸入!

// 攻擊者構造:
// O:8:"SplFileObject":2:{s:4:"file";s:11:"/etc/passwd";s:8:"flags";i:0;}
// 利用 SplFileObject __destruct() 讀取文件
?>

防護最佳實踐

  1. 絕不反序列化不可信輸入 (Cookie、GET/POST、HTTP Body、數據庫字段)
  2. 使用 JSON 替代:json_encode()/json_decode() 無代碼執行風險
  3. 若必須用 serialize:
    • 簽名驗證:`hash_hmac('sha256', $data, $secret)` 存儲簽名,反序列化前驗證
    • 白名單類:PHP 7.0+ unserialize($data, ['allowed_classes' => ['User', 'Order']])
    • 禁用魔術方法:基類實現空 `__wakeup()`/`__destruct()` 並 final
  4. 升級 PHP 版本:新版修復多個反序列化相關 CVE

現代替代方案對比

方案優點缺點適用場景
JSON安全、跨語言、可讀、標準不支援對象方法、資源、循環引用API、配置、緩存、隊列 (首選)
igbinary二進制、體積小 50%、速度快、支援對象需安裝擴展、仍有反序列化風險高性能緩存、Session (配合簽名)
MessagePack跨語言、二進制、Schema-less需擴展、PHP 對象還原需映射異構微服務通訊
Protocol Buffers強 Schema、極快、向後兼容需定義 .proto、學習曲線高性能內部 RPC、存儲
serialize()原生、支援完整對象圖安全風險、體積大、PHP 專用遺遺系統維護、極簡場景 (配合簽名+白名單)

PHP 7.4+ 新機制:__serialize / __unserialize

<?php
class User {
    public $id;
    public $name;
    private $passwordHash;  // 敏感字段
    
    public function __serialize(): array {
        return [
            'id'   => $this->id,
            'name' => $this->name,
            // 故意排除 passwordHash
        ];
    }
    
    public function __unserialize(array $data): void {
        $this->id   = $data['id'];
        $this->name = $data['name'];
        $this->passwordHash = '';  // 重置
    }
}
?>

優於 `__sleep`/`__wakeup`:類型安全、不可繼承衝突、配合 `allowed_classes` 更精準。

總結

  • serialize()/unserialize() 功能強大但**安全風險極高**
  • **首選 JSON**,跨語言、安全、生態完善
  • 必須用時:簽名驗證 + 白名單類 + 禁用魔術方法 三重防護
  • PHP 7.4+ 遷移至 __serialize/__unserialize 獲得更好控制
  • 高性能場景評估 igbinary/MessagePack/Protobuf
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved