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

PHP serialize 與 unserialize 深度解析:序列化儲存與安全風險防範

     編輯:PHP 相關知識

前言:為什麼需要序列化?

PHP 的 serialize() 將變數(陣列、物件、純量)轉為可儲存的字串格式,unserialize() 則還原。常用於 Session 儲存、快取、API 傳遞、資料庫欄位存陣列等場景。但序列化也隱藏著嚴重安全風險,本文深度解析原理、用法與防範之道。

基本語法與範例

<?php
// 陣列序列化
$array = ['name' => 'John', 'age' => 30, 'tags' => ['php', 'dev']];
$serialized = serialize($array);
// 輸出:a:3:{s:4:"name";s:4:"John";s:3:"age";i:30;s:4:"tags";a:2:{i:0;s:3:"php";i:1;s:3:"dev";}}

// 還原
$restored = unserialize($serialized);
var_dump($restored); // 結構完全一致

// 物件序列化(會呼叫 __sleep / __wakeup)
class User {
    public $name;
    private $token;
    public function __construct($name, $token) {
        $this->name = $name; $this->token = $token;
    }
    public function __sleep() { return ['name']; } // 不序列化敏感 token
    public function __wakeup() { $this->token = 're-generated'; }
}

$user = new User('Alice', 'secret123');
$ser = serialize($user);
// O:4:"User":1:{s:4:"name";s:5:"Alice";}  // token 未被序列化
$newUser = unserialize($ser); // 觸發 __wakeup

序列化格式詳解

類型標記範例
NullNN;
布林bb:1;
整數ii:42;
浮點數dd:3.14;
字串ss:5:"hello";
陣列aa:2:{i:0;s:3:"foo";i:1;s:3:"bar";}
物件OO:8:"stdClass":1:{s:3:"foo";s:3:"bar";}
參考R/rr:1; 參考第 1 個元素

進階特性

1. 參考保持

$a = ['foo' => 'bar'];
$b = &$a;
$ser = serialize([$a, $b]);
// 還原後 $restored[0] 和 $restored[1] 仍為同一參考

2. 自定義序列化邏輯

class Data {
    private $pdo; // 資源無法序列化
    private $config;

    public function __sleep() {
        return ['config']; // 僅儲存 config
    }
    public function __wakeup() {
        $this->pdo = new PDO(...); // 重新建立連線
    }
}

3. Serializable 介面 (PHP 5.1+)

class CustomSerialize implements Serializable {
    private $data;
    public function serialize() {
        return json_encode($data); // 自訂格式
    }
    public function unserialize($serialized) {
        $this->data = json_decode($serialized, true);
    }
}

實際應用場景

Session 儲存複雜結構

// PHP 內建 Session handler 自動 serialize/unserialize
$_SESSION['cart'] = ['items' => [...], 'coupon' => 'SAVE10'];

快取陣列/物件

// Redis/Memcached 值只能是字串
$cache->set('user:100', serialize($userObject));
$user = unserialize($cache->get('user:100'));

資料庫欄位存陣列 (不推薦,但常見)

// settings 欄位存序列化陣列
$settings = ['theme' => 'dark', 'lang' => 'zh-TW'];
$db->update('users', ['settings' => serialize($settings)], ['id' => 1]);

⚠️ 嚴重安全風險:物件注入

絕對不要對不可信輸入呼叫 unserialize()!

攻擊原理

惡意使用者構造序列化字串,指定任意類別並設定屬性,觸發 __wakeup()、__destruct()、__toString() 等魔術方法,導致 RCE、檔案刪除、SQL 注入等。

經典攻擊載荷

// 假設系統有類別 VulnerableClass 有 __destruct() 執行 system()
O:17:"VulnerableClass":1:{s:4:"cmd";s:10:"rm -rf /";}

真實案例

  • Laravel < CVE-2018-15133:APP_KEY 洩露導致反序列化 RCE
  • ThinkPHP 5.x 反序列化鏈
  • WordPress 多個外掛歷史漏洞

防範最佳實踐

1. 完全避免 unserialize 不可信資料

// ❌ 危險
$userData = unserialize($_COOKIE['profile']);

// ✅ 安全:使用 JSON
$userData = json_decode($_COOKIE['profile'], true);

2. PHP 7.0+ allowed_classes 選項

// 僅允許特定類別還原(仍有風險,僅作為防禦纵深)
$data = unserialize($input, ['allowed_classes' => ['MyApp\\DTO\\UserProfile']]);

3. 簽名驗證 (HMAC)

function safeSerialize($data, $secret) {
    $serialized = serialize($data);
    $hmac = hash_hmac('sha256', $serialized, $secret);
    return $hmac . ':' . $serialized;
}

function safeUnserialize($signed, $secret) {
    list($hmac, $serialized) = explode(':', $signed, 2);
    if (!hash_equals($hmac, hash_hmac('sha256', $serialized, $secret))) {
        throw new Exception('簽名驗證失敗');
    }
    return unserialize($serialized, ['allowed_classes' => false]);
}

4. 改用 JSON / MessagePack / Protocol Buffers

格式優點缺點
JSON通用、安全、人類可讀無型別資訊、體積較大
MessagePack二進制、快速、支援型別需擴展、不可直接閱讀
Protocol Buffers強型別、跨語言、極小體積需定義 .proto、學習曲線
igbinaryPHP 專用、比 serialize 快/小仍有物件注入風險

效能比較 (參考)

// 100萬次迴圈測試
serialize/unserialize:  ~1.2s / 1.5s
json_encode/decode:     ~0.8s / 1.0s
igbinary:               ~0.6s / 0.7s
msgpack:                ~0.5s / 0.6s

總結

  • serialize() 方便但極度危險,僅用於完全可信資料(如內部快取、Session)
  • 對外部輸入(Cookie、POST、API、資料庫使用者欄位)嚴禁使用
  • 優先選擇 JSON 或二進制安全格式
  • 若必須用,務必加上 HMAC 簽名 與 allowed_classes 限制
  • 定期掃描代碼庫中的 unserialize( 呼叫,確認來源可信
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved