萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 網路程式設計 >> PHP 程式設計

PHP Session 應用詳解:從 PHP3 phplib 到 PHP 8 現代最佳實踐

PHP Session 應用詳解:從歷史輔助庫到現代原生最佳實踐

Session 是 HTTP 無狀態協議下維持用戶狀態的核心機制。本文回顧 PHP3 時代 phplib 實作、PHP4 原生函數演進,並給出 PHP 7/8 時代的完整安全配置、自定義儲存處理器 (Redis/Database) 與常見坑點指南。

歷史回顧

  • PHP3:無內建 Session,依賴 phplib (page_open/page_close, DB 存儲)。
  • PHP4:引入 session_start(), session_register() (已棄用), $_SESSION 超全域陣列 (PHP 4.1+)。預設檔案儲存 /tmp。
  • PHP 5.4+:session_register() 移除,強制使用 $_SESSION['key'] = $val。
  • PHP 7+:session_start(['read_and_close' => true])、session_gc()、session_create_id()、session_set_save_handler() 物件導向介面 SessionHandlerInterface。

現代基礎用法 (PHP 7/8)

<?php
// 1. 最開頭啟動 (輸出任何內容前)
session_start([
    'cookie_lifetime' => 3600,      // 1 小時
    'cookie_secure'   => true,      // 僅 HTTPS
    'cookie_httponly' => true,      // 防 XSS 竊取
    'cookie_samesite' => 'Lax',     // CSRF 防護
    'use_strict_mode' => true,      // 拒絕未初始化的 Session ID
    'sid_length'      => 48,        // ID 長度 (更難猜測)
    'sid_bits_per_character' => 6,  // 字元熵
]);

// 2. 寫入
$_SESSION['user_id'] = 123;
$_SESSION['username'] = 'john';
$_SESSION['permissions'] = ['read', 'write'];

// 3. 讀取
$userId = $_SESSION['user_id'] ?? null;

// 4. 刪除單一鍵
unset($_SESSION['temp_data']);

// 5. 銷毀整個 Session (登出)
session_unset();     // 清空 $_SESSION
session_destroy();   // 刪除伺服器端存儲 & 發送刪除 Cookie
?>

php.ini 關鍵安全配置 (生產環境建議值)

自定義 Session 儲存處理器:Redis 範例 (高併發、分散式、共享 Session)

<?php
class RedisSessionHandler implements SessionHandlerInterface {
    private \Redis $redis;
    private string $prefix = 'sess:';
    private int $ttl = 1800; // 30 分鐘

    public function __construct(\Redis $redis, string $prefix = 'sess:', int $ttl = 1800) {
        $this->redis = $redis;
        $this->prefix = $prefix;
        $this->ttl = $ttl;
    }

    public function open($savePath, $sessionName): bool { return true; }
    public function close(): bool { return true; }

    public function read($sessionId): string|false {
        $data = $this->redis->get($this->prefix . $sessionId);
        return $data ?? '';
    }

    public function write($sessionId, $data): bool {
        return $this->redis->setex($this->prefix . $sessionId, $this->ttl, $data);
    }

    public function destroy($sessionId): bool {
        return (bool)$this->redis->del($this->prefix . $sessionId);
    }

    public function gc($maxLifetime): int|false {
        // Redis 靠 TTL 自動過期,無需掃描
        return true;
    }
}

// 註冊
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$handler = new RedisSessionHandler($redis);
session_set_save_handler($handler, true); // true = 註冊 shutdown function
session_start();
?>

資料庫儲存處理器 (MySQL) 核心 SQL

CREATE TABLE `sessions` (
    `sess_id` VARCHAR(128) NOT NULL PRIMARY KEY,
    `data` BLOB NOT NULL,
    `expiry` INT UNSIGNED NOT NULL,
    `last_activity` INT UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- write: INSERT ... ON DUPLICATE KEY UPDATE data=VALUES(data), expiry=VALUES(expiry), last_activity=VALUES(last_activity)
-- read: SELECT data FROM sessions WHERE sess_id=? AND expiry > UNIX_TIMESTAMP()
-- destroy: DELETE FROM sessions WHERE sess_id=?
-- gc: DELETE FROM sessions WHERE expiry <= UNIX_TIMESTAMP()

常見坑點與解決

  1. Headers already sent:session_start() 前有輸出 (空白、BOM、echo)。解法:ob_start() 或調整代碼順序。
  2. Session 資料不見:多台伺服器用檔案儲存未共享 → 改用 Redis/DB;session.gc_maxlifetime 過短;子域名未共享 Cookie (session_set_cookie_params(0, '/', '.example.com'))。
  3. 並發鎖死:預設檔案處理器對同一 Session 串行鎖定。長時間請求 (如大檔下載) 會阻塞同用戶其他請求。解法:session_write_close() 盡早釋放鎖,或用 Redis (無鎖/樂觀鎖)。
  4. Session Fixation:攻擊者固定 SID 誘導受害者登入。防禦:use_strict_mode=1、登入後 session_regenerate_id(true)。
  5. 序列化格式:session.serialize_handler (php/igbinary/msgpack)。igbinary/msgpack 體積更小、速度更快,需安裝擴充。

分散式系統 Session 共享架構圖


[Client] <-- Cookie: PHPSESSID --> [Load Balancer]
                                         |
                    +--------------------+--------------------+
                    |                    |                    |
              [PHP-FPM #1]          [PHP-FPM #2]          [PHP-FPM #3]
                    |                    |                    |
                    +--------------------+--------------------+
                                         |
                                  [Redis Cluster / MySQL]
                                    (Session Storage)

總結

Session 從 phplib 到原生函數再到 SessionHandlerInterface,核心挑戰始終是安全性 (防竊取/固定/注入)、可擴展性 (分散式共享)、效能 (鎖爭用、GC)。現代專案:預設用 Redis + 嚴格模式 + 安全 Cookie 屬性,極少自寫處理器,直接用成熟框架 (Laravel/Symfony) 整合的 Session 元件。

copyright © 萬盛學電腦網 all rights reserved
指令建議值說明
session.use_strict_mode1拒絕客戶端提供的未初始化 SID,防 Session Fixation
session.cookie_secure1僅 HTTPS 傳輸 Cookie
session.cookie_httponly1JS 無法讀取 document.cookie
session.cookie_samesiteLax 或 Strict防 CSRF,Lax 允許導航連結攜帶
session.gc_maxlifetime1440 (視業務)垃圾回收最大壽命 (秒)
session.gc_probability / gc_divisor1 / 100每 100 次請求觸發 1 次 GC
session.save_handlerfiles / redis / memcached儲存引擎
session.save_path/var/lib/php/sessions (或 redis://...)儲存路徑,需 0700 權限、屬 www-data
session.sid_length48Session ID 字元長度
session.sid_bits_per_character6每字元位元數 (6 = a-zA-Z0-9,-)