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

PHP 序列化與反序列化深度指南:serialize/unserialize、JSON、igbinary 與安全防範

     編輯:PHP 相關知識

資料結構持久化的核心技術與安全陷阱

序列化 是將物件/陣列轉為可儲存/傳輸的字串;反序列化 是還原過程。PHP 提供 serialize()/unserialize() 原生格式,但現代架構常選 JSON、MessagePack、igbinary。本文比較各方案特性,並重點剖析 反序列化漏洞 (Object Injection) 防範。

原生 serialize/unserialize

<?php
$data = ['user' => 'Alice', 'roles' => ['admin', 'editor'], 'meta' => (object)['id' => 123]];
$serialized = serialize($data);
// 輸出: a:3:{s:4:"user";s:5:"Alice";s:5:"roles";a:2:{i:0;s:5:"admin";i:1;s:6:"editor";}s:4:"meta";O:8:"stdClass":1:{s:2:"id";i:123;}}

$restored = unserialize($serialized);
var_dump($restored === $data); // true (值相等),但物件身份不同

// 物件序列化需類別定義存在
class User {
    public string $name;
    protected string $email;
    private int $id;
    public function __construct(string $name, string $email, int $id) {
        $this->name = $name; $this->email = $email; $this->id = $id;
    }
    public function __sleep() { return ['name', 'email']; } // 排除私有 $id
    public function __wakeup() { $this->id = random_int(1, 1000); } // 還原時重新生成
}
$user = new User('Bob', 'bob@example.com', 42);
$ser = serialize($user); // O:4:"User":2:{s:4:"name";s:3:"Bob";s:5:"email";s:16:"bob@example.com";}
$obj = unserialize($ser); // 呼叫 __wakeup()
?>

魔術方法控制序列化行為

方法觸發時機回傳值/用途
__sleep()serialize() 前陣列:指定要序列化的屬性名 (排除敏感/資源屬性)
__wakeup()unserialize() 後void:重新建立資源連線、初始化狀態
__serialize()PHP 7.4+,serialize() 時陣列:完全自定義序列化資料結構 (取代 __sleep)
__unserialize()PHP 7.4+,unserialize() 時void:從陣列還原狀態 (取代 __wakeup)

編碼陷阱與 Base64 方案

序列化字串含特殊字符 (:",;{}) 若直接存入 DB/Redis/URL 易導致截斷或注入。常見方案:

<?php
// 安全儲存/傳輸封裝
function safeSerialize(mixed $data): string {
    return base64_encode(serialize($data));
}
function safeUnserialize(string $str): mixed {
    $data = @base64_decode($str, true); // strict 模式
    if ($data === false) throw new InvalidArgumentException('Invalid base64');
    return @unserialize($data);
}

// 進階:壓縮 + Base64 (大物件)
function compactSerialize(mixed $data): string {
    return base64_encode(gzcompress(serialize($data), 6)); // 壓縮等級 6
}
function compactUnserialize(string $str): mixed {
    $decoded = base64_decode($str, true);
    if ($decoded === false) throw new InvalidArgumentException('Invalid base64');
    $decompressed = @gzuncompress($decoded);
    if ($decompressed === false) throw new RuntimeException('Decompress failed');
    return @unserialize($decompressed);
}
?>

格式對比:選擇適合你的序列化協議

維度PHP serializeJSONigbinaryMessagePack
PHP 專屬是否 (通用)是否 (通用)
支援物件/類別完整 (含私有/保護屬性)否 (僅 public 屬性,stdClass)完整否 (需映射)
體積中等較大 (文字)小 (二進位、無標籤)極小 (二進位、Schema-less)
速度 (序列化/反序列化)中快 (C 實現)極快 (C 擴充,~2-3x serialize)極快
人類可讀半可讀是否否
跨語言否是 (標準)否是 (多語言庫)
安裝內建內建需擴充 pecl install igbinary需擴充 pecl install msgpack
適用場景Session、緩存物件、舊專案相容API、配置、跨語言、日誌高效能緩存 (Redis/Memcached)、Session微服務通訊、高吞吐佇列、嵌入式

⚠️ 反序列化漏洞 (PHP Object Injection) — 必讀

當不可信資料傳入 unserialize(),攻擊者可構造惡意物件鏈,觸發 __wakeup()/__destruct()/__toString() 等魔術方法,導致 RCE、檔案讀寫、SQL 注入等。

攻擊向量示意

// 攻擊者構造 payload (簡化版)
class Evil {
    public $cmd = 'rm -rf /';
    public function __destruct() { system($this->cmd); }
}
// 序列化後: O:4:"Evil":1:{s:3:"cmd";s:8:"rm -rf /";}
// 若應用: unserialize($_COOKIE['data']) -> 災難

防範鐵律

  1. 絕不對不可信資料使用 unserialize():Cookie、Request 參數、HTTP Header、資料庫使用者可控欄位。
  2. 改用 JSON:json_decode($input, true) 只產生陣列/標量,無物件實例化風險。
  3. 若必須用 unserialize (舊 Session/緩存):
    • PHP 7.0+:unserialize($data, ['allowed_classes' => false]) 禁止所有類別實例化 (只還原陣列/標量)。
    • PHP 7.1+:['allowed_classes' => ['MySafeClass', 'AnotherClass']] 白名單模式。
    • 簽名驗證:儲存時 hash_hmac('sha256', serialize($data), $secret),讀取時先驗簽名再反序列化。
  4. Session 安全:session.serialize_handler = php_serialize (預設),搭配 session.use_strict_mode = 1、HTTPS Only Cookie、定期輪換 Session ID。
  5. 禁用危險類別:若專案不需特定類別,可在 php.ini 設定 unserialize_allowed_classes (PHP 8.4+ 提議) 或自行封裝安全解析器。

最佳實踐總結

  • API/配置/跨語言:JSON (內建、安全、可讀)。
  • 高效能 PHP 內部緩存/Session:igbinary (安裝擴充、設定 session.serialize_handler=igbinary)。
  • 微服務/gRPC 替代:MessagePack/Protobuf。
  • 舊專案相容:serialize() + Base64 + HMAC 簽名 + allowed_classes 白名單。
  • 永遠記住:unserialize($userInput) = 遠端代碼執行風險。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved