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

PHP Session 完整指南:原理、安全性與實戰應用

     編輯:PHP 綜合知識

PHP Session 機制深度解析與最佳實踐

Session 是 Web 開發中維持用戶狀態的核心機制。本文詳細說明 Session 原理、為何比 Cookie 安全、配置細節、登入驗證實作、常見陷阱與優化建議。

Session vs Cookie:核心差異

特性 Cookie Session
儲存位置 客戶端瀏覽器 伺服器端 (檔案/資料庫/Redis)
資料可見性 用戶可看、改、竊取 用戶無法直接存取
容量限制 單個 4KB,總量約 300 個 受限於伺服器磁碟/記憶體
生命週期 可設定持久化 通常隨瀏覽器關閉失效 (可調整)
安全性 低 (需加密、簽名、HttpOnly) 高 (資料不經網路傳輸)

Session 運作流程

  1. session_start() → 檢查 Cookie 中 PHPSESSID
  2. 有 ID → 讀取對應 Session 檔案還原 $_SESSION
  3. 無 ID → 產生新 ID、建立空 Session 檔案、設定 Cookie
  4. 腳本執行中操作 $_SESSION
  5. 腳本結束 / session_write_close() → 序列化寫回儲存

基礎配置 (php.ini)

; 儲存路徑 (預設系統暫存目錄,建議自訂)
session.save_path = "/var/lib/php/sessions"

; 垃圾回收機率 (1/1000 請求觸發)
session.gc_probability = 1
session.gc_divisor = 1000

; Session 過期秒數 (24分鐘)
session.gc_maxlifetime = 1440

; Cookie 參數
session.cookie_lifetime = 0      ; 0 = 瀏覽器關閉失效
session.cookie_httponly = 1      ; 防 XSS 竊取
session.cookie_secure = 1        ; 僅 HTTPS 傳輸 (需 SSL)
session.use_strict_mode = 1      ; 防 Session Fixation

登入驗證完整實作

<?php
// login.php
session_start();

// 已登入導向首頁
if (isset($_SESSION['admin']) && $_SESSION['admin'] === true) {
    header('Location: index.php');
    exit;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username'] ?? '');
    $password = $_POST['password'] ?? '';

    // 1. 驗證輸入
    if (empty($username) || empty($password)) {
        $error = '請輸入帳號密碼';
    } else {
        // 2. 查詢資料庫 (使用 PDO 預處理)
        $pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
        $stmt = $pdo->prepare("SELECT id, password_hash FROM users WHERE username = ?");
        $stmt->execute([$username]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);

        // 3. 驗證密碼
        if ($user && password_verify($password, $user['password_hash'])) {
            // 4. 登入成功:重新產生 Session ID (防 Fixation)
            session_regenerate_id(true);
            
            // 5. 設定 Session 變數
            $_SESSION['admin'] = true;
            $_SESSION['user_id'] = $user['id'];
            $_SESSION['username'] = $username;
            $_SESSION['login_time'] = time();
            
            header('Location: dashboard.php');
            exit;
        } else {
            $error = '帳號或密碼錯誤';
        }
    }
}
?>
<form method="post">
    帳號: <input name="username"><br>
    密碼: <input name="password" type="password"><br>
    <button type="submit">登入</button>
    $error
"; ?> </form>

受保護頁面驗證

<?php
// dashboard.php
session_start();

// 檢查登入狀態
if (!isset($_SESSION['admin']) || $_SESSION['admin'] !== true) {
    header('Location: login.php');
    exit;
}

// 選擇性:檢查 Session 逾時 (30 分鐘無操作)
if (time() - $_SESSION['login_time'] > 1800) {
    session_destroy();
    header('Location: login.php?timeout=1');
    exit;
}

// 更新最後活動時間
$_SESSION['login_time'] = time();
?>
<h1>歡迎, </h1>
<a href="logout.php">登出</a>

登出處理

<?php
// logout.php
session_start();
$_SESSION = [];              // 清空所有 Session 變數
session_destroy();           // 刪除伺服器端 Session 檔案
setcookie(session_name(), '', time()-3600, '/'); // 刪除客戶端 Cookie
header('Location: login.php');
exit;
?>

常見問題與解決方案

問題 原因 解決方案
Session 變數消失 未呼叫 session_start() 每支需用 Session 的腳本開頭必須呼叫
Headers already sent session_start() 前有輸出 (空白、BOM、echo) 確保 session_start() 在最前面,檔案存 UTF-8 無 BOM
多伺服器 Session 不共享 預設存檔案系統,各伺服器獨立 使用 Redis/Memcached/資料庫共享儲存
Session 劫持 Session ID 被竊取 (XSS、中間人) HTTPS + HttpOnly + Secure + 定期重生 ID
Session 固定攻擊 攻擊者強迫受害者使用已知 Session ID session_regenerate_id(true) 登入後重生 ID

高階:自訂 Session Handler (Redis 範例)

<?php
class RedisSessionHandler implements SessionHandlerInterface {
    private $redis;
    private $prefix = 'session:';
    private $ttl = 1800; // 30 分鐘

    public function __construct($host='127.0.0.1', $port=6379) {
        $this->redis = new Redis();
        $this->redis->connect($host, $port);
    }

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

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

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

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

    public function gc($maxlifetime) { return true; } // Redis 自動過期
}

// 註冊
ini_set('session.save_handler', 'user');
session_set_save_handler(new RedisSessionHandler(), true);
session_start();
?>
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved