Cookie 與 Session 是 Web 會話管理的兩大基石。理解它們的底層機制、優缺點與適用場景,對於構建安全、高效的用戶認證系統至關重要。
核心差異對比表
| 維度 | Cookie | Session |
| 存儲位置 | 客戶端瀏覽器(文本文件/內存) | 服務端(文件、Redis、數據庫、Memcached) |
| 數據可見性 | 用戶可查看、修改、刪除 | 用戶不可直接訪問,僅持有 Session ID |
| 安全性 | 低:易遭竄改、盜取、CSRF/XSS 利用 | 高:敏感數據不落地客戶端 |
| 存儲容量 | 單個 ≤ 4KB,域名下總計 ≤ 20-50 個 | 理論無上限(受服務端存儲介質限制) |
| 生命周期控制 | 設置 Expires/Max-Age,瀏覽器決定是否過期 | 服務端控制過期時間,可強制銷毀 |
| 跨域支持 | 受同源策略限制,可配置 Domain/Path | 需共享存儲或 Token 傳遞實現跨域 |
| 服務端壓力 | 無(數據在客戶端) | 有:高併發下需優化存儲(Redis 集群等) |
| 關鍵依賴 | 瀏覽器支持 Cookie | 默認依賴 Cookie 傳遞 Session ID |
Cookie 詳細用法
設置 Cookie
<?php
// setcookie(name, value, expire, path, domain, secure, httponly)
// expire: 時間戳,time()+3600 表示 1 小時後過期
// 必須在任何輸出之前調用(包括空白字符、BOM)
setcookie('user', 'zhangsan', time() + 3600, '/', '', false, true); // httponly 防 XSS 竊取
?>
httponly=true:禁止 JavaScript 讀取,緩解 XSS 風險。
secure=true:僅 HTTPS 傳輸(需 HTTPS 環境)。
samesite='Lax'(PHP 7.3+):setcookie(..., ['samesite' => 'Lax']),防 CSRF。
讀取與刪除
<?php
// 讀取
$user = $_COOKIE['user'] ?? '';
// 刪除:設置過期時間為過去
setcookie('user', '', time() - 3600, '/');
// 或
setcookie('user'); // 只刪除會話級 Cookie
?>
刪除 Session ID Cookie(登出時)
<?php
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time() - 3600, '/');
}
?>
Session 詳細用法
啟動與基本操作
<?php
// 1. 必須在輸出前調用
session_start();
// 2. 讀寫
$_SESSION['user_id'] = 123;
$_SESSION['username'] = 'zhangsan';
echo $_SESSION['username'];
// 3. 銷毀
// 僅清空數據
$_SESSION = array();
// 徹底銷毀:刪除服務端文件 + 客戶端 Cookie
session_destroy();
// 完整登出流程
session_start();
$_SESSION = array();
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
session_destroy();
?>
安全性深度分析:為什麼 Session 更安全?
- 數據不落地客戶端: 敏感信息(用戶 ID、權限、購物車)存儲在服務端,攻擊者無法直接竊取篡改。
- Session ID 隨機性強: PHP 默認使用 128 位隨機數(
session.entropy_file + session.hash_function),暴力破解極難。
- 會話綁定上下文: 可綁定 IP、User-Agent、指紋,異常請求強制失效。
- 服務端可控: 可隨時強制下線、限制並發會話、審計日志。
- Cookie 只是載體: Session ID 存儲在 Cookie 中,破解 Session 必須先破解 Cookie(竊取 ID),再在有效期內利用,難度指數級上升。
性能優化:Session 存儲介質選擇
| 存儲驅動 | 適用場景 | 優點 | 缺點 |
| files (默認) | 單機、低併發、開發環境 | 零配置 | 磁盤 IO 瓶頸、難以集群共享 |
| Redis | 高併發、分佈式、生產環境首選 | 內存級速度、原子操作、集群支持、TTL 自動過期 | 需額外組件、持久化配置 |
| Memcached | 高併發、純緩存場景 | 簡單、快速 | 無持久化、重啟丟失、無原子增減 |
| Database | 需持久化、審計、關聯查詢 | ACID、可查詢 | 性能最差、鎖競爭 |
切換到 Redis 示例
<?php
// php.ini 或運行時設置
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', 'tcp://127.0.0.1:6379?auth=password&database=0');
// 或集群
// ini_set('session.save_path', 'tcp://host1:6379,tcp://host2:6379?weight=1&timeout=2.5');
session_start();
// 後續 $_SESSION 操作自動讀寫 Redis
?>
最佳實踐總結
- 敏感數據只存 Session: 用戶 ID、權限、Token、購物車等業務數據。
- 非敏感偏好存 Cookie: 語言、主題、最近瀏覽、追蹤 ID 等無風險數據。
- 啟用安全屬性:
httponly、secure、samesite 三大屬性全開。
- Session 配置加固:
<?php
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1); // 僅 HTTPS
ini_set('session.cookie_samesite', 'Lax');
ini_set('session.use_strict_mode', 1); // 防 Session Fixation
ini_set('session.gc_maxlifetime', 1800); // 30 分鐘無活動過期
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);
?>
- 生產環境必用 Redis/Memcached 存儲 Session,避免文件鎖與單點故障。
- 實現「記住我」: 登入時生成長效 Token 存入 Cookie(加密簽名),Session 設置短過期;驗證 Token 自動重建 Session。
常見誤區澄清
| 誤區 | 真相 |
| 「Session 不依賴 Cookie」 | 默認靠 Cookie 傳遞 ID;可用 URL 重寫(session.use_trans_sid=1)但不安全、不美觀、已棄用。 |
| 「Cookie 更快因無服務端開銷」 | 數據量大時 Cookie 隨每請求傳輸,增加帶寬延遲;Session 只傳 ID。 |
| 「關閉瀏覽器 Session 就刪除了」td> | 會話級 Cookie 關閉瀏覽器刪除,但服務端文件仍存留至 GC 清理。 |
| 「加密 Cookie 就安全了」 | 加密只防竊取內容,防不住重放攻擊、CSRF;且密鑰洩露即全盤崩潰。 |
Cookie 與 Session 不是非此即彼的對立關係,而是協作關係:Session ID 乘坐 Cookie 這輛車,在客戶端與服務端之間穿梭。合理分工、加固配置、選對存儲,才能構建穩固的會話管理體系。