PHP 4 引入物件序列化,允許將物件狀態轉為字串儲存。但僅序列化屬性而非方法,導致反序列化後需重新載入類別定義。本文深度剖析機制、安全風險與最佳實踐。
<?php
class User {
public $name;
public $email;
private $passwordHash; // 私有屬性也會序列化
public function __construct($name, $email, $password) {
$this->name = $name;
$this->email = $email;
$this->passwordHash = password_hash($password, PASSWORD_DEFAULT);
}
public function greet() {
return "Hello, {$this->name}!";
}
}
$user = new User('Alice', 'alice@example.com', 'secret123');
$serialized = serialize($user);
echo "序列化字串:\n$serialized\n\n";
// 輸出範例:
// O:4:"User":3:{s:4:"name";s:5:"Alice";s:5:"email";s:19:"alice@example.com";s:16:"UserpasswordHash";s:60:"$2y$10$...";}
// 儲存至檔案/資料庫
file_put_contents('user.dat', $serialized);
// --- 跨請求/腳本恢復 ---
$loaded = file_get_contents('user.dat');
$user2 = unserialize($loaded);
// 關鍵:類別定義必須已載入 (require 'User.php')
echo $user2->greet(); // 正常運作:方法來自類別定義,非序列化資料
?>
| 前綴 | 類型 | 範例 |
|---|---|---|
| i | Integer | i:42; |
| d | Float | d:3.14; |
| b | Boolean | b:1; |
| s | String | s:5:"hello"; |
| a | Array | a:2:{i:0;s:3:"foo";i:1;s:3:"bar";} |
| O | Object | O:8:"stdClass":1:{s:3:"foo";s:3:"bar";} |
| r | Reference | r:1; |
| N | NULL | N; |
序列化字串僅包含屬性值。反序列化時,PHP 依據類別名稱尋找已載入的類別定義,並將屬性值注入新實例。若類別定義不存在、簽名不符、或方法邏輯已變更,行為將異常。
資料庫連線、檔案控制代碼、GD 圖片資源等內部指標無法序列化。嘗試序列化會拋出 Exception (PHP 7.4+) 或產生不完整字串。
class User {
// 序列化前調用:決定哪些屬性被序列化
public function __sleep() {
return ['name', 'email']; // 排除 passwordHash、暫時屬性
}
// 反序列化後調用:重建資源連線、初始化狀態
public function __wakeup() {
$this->db = new PDO(...); // 重新建立 DB 連線
}
// PHP 7.4+ 新介面 (推薦替代 __sleep/__wakeup)
public function __serialize(): array {
return ['name' => $this->name, 'email' => $this->email];
}
public function __unserialize(array $data): void {
$this->name = $data['name'];
$this->email = $data['email'];
$this->db = new PDO(...);
}
}
?>
**不可信資料絕對禁止 unserialize()**。攻擊者構造惡意序列化字串,利用
__wakeup/__destruct/__toString魔術方法鏈 (Gadget Chain) 達成 RCE、檔案寫入、SQL 注入。
- 已知漏洞框架:Laravel (歷史版本)、Yii、Symfony、WordPress 外掛
- 防護:
unserialize($data, ['allowed_classes' => ['MyClass']])白名單 (PHP 7.0+)- 替代:JSON (
json_encode/decode)、MessagePack、Protocol Buffers — 僅資料、無程式碼執行風險
| 場景 | 推薦方案 | 理由 |
|---|---|---|
| Session 儲存 | PHP 內建 session handler (files/redis/memcached) | 自動序列化、GC、鎖機制 |
| 快取物件 | Redis + JSON/MessagePack | 高效能、跨語言、無反序列化風險 |
| 排程任務傳遞 | 資料庫 JSON 欄位 / 訊息佇列 (RabbitMQ/Kafka) | 持久、可查詢、解耦 |
| API 回應 | JSON (標準) | 通用、可快取、前端直食 |
| 複雜領域物件 | Event Sourcing + CQRS | 完整審計軌跡、時間旅行、重放 |