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

PHP Cookie 與 Session 深度解析:原理、區別與最佳實踐

     編輯:PHP 綜合知識

核心概念對比

維度CookieSession
存儲位置客戶端(瀏覽器)服務端(文件/Redis/資料庫)
存儲容量單個 ≤ 4KB,域名下 ≤ 20-50 個理論無上限(受服務端記憶體/磁盤限制)
生命週期可設置過期時間,持久化默認瀏覽器關閉失效,可配置 gc_maxlifetime
安全性可被竄改、攔截、XSS 盜取數據不暴露給客戶端,相對安全
傳輸開銷每次請求自動攜帶,增加帶寬僅傳輸 Session ID(極小)
跨域支援受同源策略限制,需配置 Domain/Path同樣依賴 Cookie 傳遞 SID,跨域同樣受限

Cookie 詳細機制

設置 Cookie

// 基礎語法
setcookie(string $name, string $value = '', int $expire = 0, string $path = '', string $domain = '', bool $secure = false, bool $httponly = false);

// 實戰範例:設置 7 天有效、全站可訪、僅 HTTPS、防 XSS
setcookie('user_pref', json_encode(['theme' => 'dark', 'lang' => 'zh']), [
    'expires' => time() + 7 * 86400,
    'path' => '/',
    'domain' => '.example.com',  // 子域共享
    'secure' => true,            // 僅 HTTPS 傳輸
    'httponly' => true,          // JS 無法讀取,防 XSS
    'samesite' => 'Lax',         // CSRF 防護
]);

// PHP 7.3+ 支援陣列選項語法,推薦使用

接收與讀取

// 自動解析到 $_COOKIE 超全局陣列
$theme = $_COOKIE['user_pref']['theme'] ?? 'light'; // 需 json_decode

// 原始 Cookie 字串(含未解析的)
$raw = $_SERVER['HTTP_COOKIE'] ?? '';

刪除 Cookie

// 方法 1:設置過期時間為過去
setcookie('user_pref', '', time() - 3600, '/', '.example.com');

// 方法 2:僅提供名稱(PHP 會發送過期 Cookie)
setcookie('user_pref');

關鍵限制與避坑

  • 必須在輸出前設置:Cookie 屬於 HTTP Header,任何 echo/空白/HTML 輸出前必須調用
  • 域名匹配規則:.example.com 包含所有子域;example.com 僅當前域
  • 路徑作用域:/admin 設置的 Cookie,/ 頁面不會攜帶
  • 大小限制:單 Cookie 4KB,不要存大量數據
  • 用戶可禁用/清除:不可依賴 Cookie 存儲關鍵業務數據

Session 詳細機制

啟動與配置

// 必須在任何輸出前調用
session_start([
    'cookie_lifetime' => 3600,      // Session ID Cookie 存活 1 小時
    'cookie_secure' => true,        // 僅 HTTPS
    'cookie_httponly' => true,      // 防 XSS
    'cookie_samesite' => 'Lax',     // CSRF 防護
    'use_strict_mode' => true,      // 拒絕未初始化的 Session ID
    'gc_maxlifetime' => 1440,       // 服務端數據最大存活 24 分鐘
    'save_path' => '/var/lib/php/sessions', // 文件存儲路徑
]);

// 生產環境建議:使用 Redis 存儲
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', 'tcp://127.0.0.1:6379?auth=password&prefix=session_');

讀寫操作

// 寫入
$_SESSION['user_id'] = 12345;
$_SESSION['permissions'] = ['read', 'write'];

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

// 刪除單項
unset($_SESSION['temp_data']);

// 銷毀整個 Session(登出)
session_unset();      // 清空 $_SESSION
session_destroy();    // 刪除服務端存儲文件
// 注意:不會自動刪除客戶端 Session ID Cookie,需手動
setcookie(session_name(), '', time() - 3600, '/');

Session ID 傳遞機制

  1. Cookie(默認):PHPSESSID=abc123 自動攜帶
  2. URL 重寫:php.ini: session.use_trans_sid = 1,自動在鏈接/表單附加 ?PHPSESSID=abc
  3. 隱藏表單:手動在表單添加

⚠️ URL 傳遞存在安全風險(日誌洩露、分享鏈接泄露),生產環境禁用,強制使用 Cookie + HTTPS。

進階主題

Session 固定攻擊防範

// 登錄成功後,重新生成 Session ID,保留數據
if (login($username, $password)) {
    session_regenerate_id(true); // true = 刪除舊 Session 文件
    $_SESSION['user_id'] = $userId;
}

分布式 Session 共享

// Redis 集群模式(需 phpredis 擴展)
ini_set('session.save_handler', 'rediscluster');
ini_set('session.save_path', 'seed[]=10.0.0.1:6379,seed[]=10.0.0.2:6379,timeout=2.5,read_timeout=2.5,prefix=session_');

// 或資料庫存儲(需自定義處理器)
class DbSessionHandler implements SessionHandlerInterface {
    private $pdo;
    public function __construct(PDO $pdo) { $this->pdo = $pdo; }
    public function open($path, $name) { return true; }
    public function close() { return true; }
    public function read($id) { /* SELECT data FROM sessions WHERE id=? */ }
    public function write($id, $data) { /* REPLACE INTO sessions ... */ }
    public function destroy($id) { /* DELETE FROM sessions WHERE id=? */ }
    public function gc($maxlifetime) { /* DELETE WHERE updated < NOW() - ? */ }
}
session_set_save_handler(new DbSessionHandler($pdo), true);

Session 与 Cookie 協作模式

// 記住我功能
if ($rememberMe) {
    // 生成長效 Token,存資料庫關聯用戶
    $token = bin2hex(random_bytes(32));
    $expires = time() + 30 * 86400;
    $pdo->prepare("INSERT INTO remember_tokens (user_id, token, expires) VALUES (?, ?, ?)")
        ->execute([$userId, hash('sha256', $token), date('Y-m-d H:i:s', $expires)]);
    
    // 發送長效 Cookie
    setcookie('remember_token', $token, $expires, '/', '', true, true);
}

// 每次請求檢查
if (!isset($_SESSION['user_id']) && isset($_COOKIE['remember_token'])) {
    $tokenHash = hash('sha256', $_COOKIE['remember_token']);
    $stmt = $pdo->prepare("SELECT user_id FROM remember_tokens WHERE token = ? AND expires > NOW()");
    $stmt->execute([$tokenHash]);
    if ($row = $stmt->fetch()) {
        $_SESSION['user_id'] = $row['user_id']; // 自動登錄
        session_regenerate_id(true);
    }
}

最佳實踐總結

  1. 敏感數據只存 Session:用戶 ID、權限、購物車等
  2. 非敏感偏好存 Cookie:主題、語言、最近瀏覽(體積小)
  3. 全站 HTTPS + Secure + HttpOnly + SameSite:基礎安全三件套
  4. Session 使用 Redis/資料庫:支援分布式、高併發、持久化
  5. 定期清理過期 Session:配置 session.gc_probability/divisor 或定時任務
  6. 登錄/權限變更時 session_regenerate_id(true):防固定攻擊
  7. 不在 URL 傳遞 Session ID:關閉 use_trans_sid
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved