| 維度 | Cookie | Session |
|---|---|---|
| 存儲位置 | 客戶端(瀏覽器) | 服務端(文件/Redis/資料庫) |
| 存儲容量 | 單個 ≤ 4KB,域名下 ≤ 20-50 個 | 理論無上限(受服務端記憶體/磁盤限制) |
| 生命週期 | 可設置過期時間,持久化 | 默認瀏覽器關閉失效,可配置 gc_maxlifetime |
| 安全性 | 可被竄改、攔截、XSS 盜取 | 數據不暴露給客戶端,相對安全 |
| 傳輸開銷 | 每次請求自動攜帶,增加帶寬 | 僅傳輸 Session ID(極小) |
| 跨域支援 | 受同源策略限制,需配置 Domain/Path | 同樣依賴 Cookie 傳遞 SID,跨域同樣受限 |
// 基礎語法
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'] ?? '';
// 方法 1:設置過期時間為過去
setcookie('user_pref', '', time() - 3600, '/', '.example.com');
// 方法 2:僅提供名稱(PHP 會發送過期 Cookie)
setcookie('user_pref');
echo/空白/HTML 輸出前必須調用.example.com 包含所有子域;example.com 僅當前域/admin 設置的 Cookie,/ 頁面不會攜帶// 必須在任何輸出前調用
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, '/');
PHPSESSID=abc123 自動攜帶php.ini: session.use_trans_sid = 1,自動在鏈接/表單附加 ?PHPSESSID=abc⚠️ URL 傳遞存在安全風險(日誌洩露、分享鏈接泄露),生產環境禁用,強制使用 Cookie + HTTPS。
// 登錄成功後,重新生成 Session ID,保留數據
if (login($username, $password)) {
session_regenerate_id(true); // true = 刪除舊 Session 文件
$_SESSION['user_id'] = $userId;
}
// 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);
// 記住我功能
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);
}
}
session.gc_probability/divisor 或定時任務session_regenerate_id(true):防固定攻擊use_trans_sid