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

PHP 基於 Session 與 Cookie 的用戶認證類深度解析與最佳實踐

     編輯:PHP 綜合知識

前言

用戶認證是 Web 應用的基石。本文在上一篇雙重驗證方案基礎上,進一步深入剖析 Session 與 Cookie 的本質區別、安全攻防細節、分佈式架構下的 Session 共享方案,並提供生產級代碼架構參考。

Session vs Cookie:本質區別深度解析

維度SessionCookie
存儲位置服務端(文件/Redis/Memcached/數據庫)客戶端瀏覽器(內存/硬盤)
生命周期瀏覽器關閉失效(默認),可配置 gc_maxlifetime可設置 Expires/Max-Age 持久化
容量限制受服務端存儲限制,通常無實際上限單個 4KB,域名下總計 ~300 個
安全性高(數據不離開服務端)低(可被竄改、偷取、XSS 讀取)
網絡開銷僅傳 Session ID(~32 字節)每請求攜帶完整數據
跨域支持需配合 CORS + SameSite=None+Secure原生支持跨域(受 SameSite 限制)
典型用途用戶身份、權限、購物車、臨時數據偏好設置、追蹤標識、記住我 Token

Session 劫持與固定攻擊實戰防護

攻擊場景復現

<?php
// 攻擊者構造鏈接誘導受害者點擊
// https://victim.com/login.php?PHPSESSID=attacker_known_id
// 受害者登錄後,攻擊者使用同一 Session ID 即可劫持會話

// 防護:啟用 strict_mode + 登錄後強制重生成 ID
ini_set('session.use_strict_mode', '1');
// 登錄成功後
session_regenerate_id(true); // 刪除舊 Session 文件
?>

完整防護中間件

<?php
namespace App\Middleware;

class SessionSecurityMiddleware {
    public function handle(callable $next): void {
        // 1. 啟用安全配置(僅執行一次)
        static $initialized = false;
        if (!$initialized) {
            ini_set('session.cookie_httponly', '1');
            ini_set('session.cookie_secure', isset($_SERVER['HTTPS']) ? '1' : '0');
            ini_set('session.cookie_samesite', 'Lax');
            ini_set('session.use_strict_mode', '1');
            ini_set('session.use_trans_sid', '0'); // 禁用 URL 透傳 SID
            ini_set('session.hash_function', 'sha256');
            ini_set('session.hash_bits_per_character', '5');
            $initialized = true;
        }

        // 2. 檢測 Session 劫持指標
        $this->detectHijacking();

        // 3. 定期輪換 Session ID(每 30 分鐘或關鍵操作後)
        $this->rotateIfNeeded();

        $next();
    }

    private function detectHijacking(): void {
        $fp = $_SESSION['__fingerprint'] ?? null;
        $current = $this->generateFingerprint();
        
        if ($fp && !hash_equals($fp, $current)) {
            // 指紋不匹配:可能被劫持
            error_log("Session 劫持嘗試: " . session_id());
            session_destroy();
            throw new \Exception('會話異常,請重新登錄');
        }
        
        $_SESSION['__fingerprint'] = $current;
    }

    private function generateFingerprint(): string {
        // 綁定 User-Agent + IP 前綴(避免動態 IP 導致誤判)
        $ipPrefix = implode('.', array_slice(explode('.', $_SERVER['REMOTE_ADDR'] ?? ''), 0, 2));
        $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
        return hash('sha256', $ipPrefix . '|' . $ua);
    }

    private function rotateIfNeeded(): void {
        $lastRotate = $_SESSION['__rotate_at'] ?? 0;
        if (time() - $lastRotate > 1800) { // 30 分鐘
            session_regenerate_id(true);
            $_SESSION['__rotate_at'] = time();
        }
    }
}
?>

分佈式架構下的 Session 共享方案

方案對比

方案優點缺點適用場景
Redis 集中存儲高性能、原子操作、支持 TTL、集群模式需維護 Redis 集群、單點風險中大型應用、微服務
數據庫持久化ACID、無額外組件、便於審計寫入延遲高、連接池壓力大強一致性要求、低併發
Sticky Session (Nginx ip_hash)無代碼改動、簡單擴展性差、節點故障導致 Session 丟失遷移過渡期、有狀態服務
JWT 無狀態水平擴展完美、無存儲依賴無法主動失效、Payload 體積大、簽名驗證 CPU 開銷API 服務、單頁應用

Redis Session 實現

<?php
namespace App\Session;

class RedisSessionHandler implements \SessionHandlerInterface {
    private \Redis $redis;
    private string $prefix;
    private int $ttl;

    public function __construct(\Redis $redis, string $prefix = 'sess:', int $ttl = 7200) {
        $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 {
        $key = $this->prefix . $sessionId;
        $data = $this->redis->get($key);
        return $data ?? '';
    }

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

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

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

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

Cookie 安全屬性完整配置

<?php
function setSecureCookie(
    string $name,
    string $value,
    int $expire = 0,
    string $path = '/',
    string $domain = '',
    bool $secure = true,
    bool $httponly = true,
    string $samesite = 'Lax'
): void {
    $options = [
        'expires' => $expire,
        'path' => $path,
        'domain' => $domain,
        'secure' => $secure,
        'httponly' => $httponly,
        'samesite' => $samesite,
    ];
    
    // PHP 7.3+ 支持 options 參數
    if (PHP_VERSION_ID >= 70300) {
        setcookie($name, $value, $options);
    } else {
        // 兼容舊版本
        $cookie = $name . '=' . rawurlencode($value);
        $cookie .= '; Max-Age=' . ($expire - time());
        $cookie .= '; Path=' . $path;
        if ($domain) $cookie .= '; Domain=' . $domain;
        if ($secure) $cookie .= '; Secure';
        if ($httponly) $cookie .= '; HttpOnly';
        $cookie .= '; SameSite=' . $samesite;
        header('Set-Cookie: ' . $cookie, false);
    }
}
?>

總結:認證架構選型決策樹

graph TD
    A[項目類型] -->|傳統 Web 多頁面| B[Session + Redis]
    A -->|API/SPA/微服務| C[JWT + 短期 Access Token + Refresh Token]
    A -->|混合架構| D[Session(Web) + JWT(API)]
    
    B --> E{併發量}
    E -->|高| F[Redis Cluster + 本地緩存]
    E -->|中低| G[單機 Redis + 持久化]
    
    C --> H{撤銷需求}
    H -->|強撤銷| I[Redis 黑名單 + 短過期]
    H -->|弱撤銷| J[純 JWT 短過期]

核心原則:敏感操作用 Session,跨域無狀態用 JWT,記住我用簽名 Cookie,集群必上 Redis,安全屬性全開啟。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved