前言
HTTP 是無狀態協定,Cookie 與 Session 是 Web 開發中實現狀態保持的兩大核心機制。本文系統梳理 PHP 中 Cookie 與 Session 的原理差異、操作 API、常見陷阱與安全建議。
一、Cookie:客戶端狀態存儲
1. 核心特點
- 資料儲存在瀏覽器端(文字檔或資料庫)。
- 每次請求自動攜帶對應域名/路徑下的 Cookie。
- 受同源策略限制,跨域不共享。
- 大小限制約 4KB,數量限制約 20~50 個/域名。
2. 設定 Cookie
<?php
// 基礎語法
bool setcookie(
string $name,
string $value = "",
int $expire = 0, // Unix 時間戳;0 = 瀏覽器關閉失效
string $path = "/", // 生效路徑
string $domain = "", // 生效域名(.example.com 含子域名)
bool $secure = false, // 僅 HTTPS 傳輸
bool $httponly = true // 禁止 JS 存取(防 XSS)
);
// 範例:有效期 1 小時,全站可用,僅 HTTPS+HttpOnly
setcookie('user_id', '12345', time() + 3600, '/', '.example.com', true, true);
// 陣列式寫入(自動序列化為多個 Cookie)
setcookie('pref[theme]', 'dark');
setcookie('pref[lang]', 'zh-TW');
// 讀取時:$_COOKIE['pref']['theme']
?>
3. 讀取與刪除
<?php
// 讀取
$userId = $_COOKIE['user_id'] ?? null;
// 刪除:設定過期時間為過去
setcookie('user_id', '', time() - 3600, '/', '.example.com', true, true);
?>
4. 常見坑點與解決
| 問題 | 原因 | 解決方案 |
headers already sent 警告 | 在 setcookie() 前有輸出(空白、BOM、echo) | 確保無輸出;或啟用 ob_start() 緩衝輸出 |
| 中文亂碼 | Cookie 僅支援 ASCII | 使用 urlencode() 儲存,urldecode() 讀取 |
magic_quotes_gpc 自動轉義 | 舊版 PHP 設定 | PHP 5.4+ 已移除;舊版用 stripslashes() 處理 |
| 用戶禁用 Cookie | 隱私設定/無痕模式 | 檢測並降級提示;或改用 Session + URL 傳遞 SID(不推薦) |
二、Session:伺服器端狀態管理
1. 運作原理
- 呼叫
session_start(),PHP 檢查請求中是否攜帶 Session ID(預設 Cookie 名 PHPSESSID)。
- 若無,生成新 ID,建立伺服器端儲存檔(預設
/tmp),並發送 Set-Cookie 標頭。
- 後續請求攜帶 ID,PHP 自動載入對應的
$_SESSION 資料。
2. 基本操作
<?php
session_start(); // 必須在任何輸出前呼叫
// 寫入
$_SESSION['user'] = ['id' => 1, 'name' => 'Alice'];
$_SESSION['cart'] = [101, 202];
// 讀取
$userName = $_SESSION['user']['name'] ?? 'Guest';
// 刪除單一鍵
unset($_SESSION['cart']);
// 徹底銷毀 Session(登出流程)
$_SESSION = []; // 清空陣列
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(); // 刪除伺服器端儲存檔
?>
3. Session ID 傳遞方式
- Cookie(預設):最常用、最安全。
- URL 參數:
?PHPSESSID=abc123,僅用於禁用 Cookie 環境,易洩露於日誌/Referer,不推薦。
4. 進階配置
<?php
// 必須在 session_start() 之前設定
ini_set('session.gc_maxlifetime', 7200); // 垃圾回收壽命(秒)
ini_set('session.cookie_lifetime', 3600); // Cookie 存活時間
ini_set('session.cookie_httponly', 1); // 防 XSS
ini_set('session.cookie_secure', 1); // 僅 HTTPS(生產環境必開)
ini_set('session.use_strict_mode', 1); // 拒絕未初始化的 ID(防固定攻擊)
// 自訂儲存處理器(資料庫、Redis、Memcached)
session_set_save_handler(
'open', 'close', 'read', 'write', 'destroy', 'gc'
);
// 或實作 SessionHandlerInterface 介面
?>
三、Cookie vs Session:選型指南
| 維度 | Cookie | Session |
| 儲存位置 | 客戶端 | 伺服器端 |
| 容量 | ~4KB | 受限於伺服器記憶體/磁碟 |
| 安全性 | 可被竄改/竊取 | 較安全(ID 若洩露仍可劫持) |
| 適用場景 | 偏好設定、追蹤、記住登入 | 用戶狀態、購物車、權限驗證 |
| 生命週期 | 可設定長期 | 通常短期(瀏覽器關閉或 gc_maxlifetime) |
四、安全最佳實踐清單
- ✅ 所有 Session Cookie 設定
HttpOnly、Secure、SameSite=Strict/Lax。
- ✅ 登入後重新生成 Session ID(
session_regenerate_id(true)),防 Session Fixation。
- ✅ 敏感操作(修改密碼、付款)要求二次驗證或 CSRF Token。
- ✅ 生產環境使用集中式 Session 儲存(Redis Cluster),支援多伺服器負載均衡。
- ✅ 定期清理過期 Session,避免檔案系統 inode 耗盡。