Cookie 是 HTTP 無狀態特性下維持狀態的關鍵機制。本文詳細說明 PHP Cookie 的設定參數、安全屬性、跨域共享、加密簽名與現代替代方案。
<?php
// 基本語法:setcookie(name, value, expires, path, domain, secure, httponly)
setcookie('user_id', '12345', [
'expires' => time() + 86400 * 30, // 30 天
'path' => '/',
'domain' => 'example.com', // 可選,預設為當前域名
'secure' => true, // 僅 HTTPS 傳送
'httponly' => true, // JavaScript 無法存取(防 XSS)
'samesite' => 'Lax' // CSRF 保護:Lax/Strict/None
]);
// PHP 7.3+ 陣列語法(推薦)
setcookie('session_token', $token, time() + 3600, '/', '', true, true);
// 舊式位置參數:setcookie('name', 'value', expire, path, domain, secure, httponly)
?>
<?php
// $_COOKIE 超全域變數(只包含無 HttpOnly 的 Cookie)
$userId = $_COOKIE['user_id'] ?? null;
// 所有 Cookie(含 HttpOnly)需從標頭解析
$cookies = [];
foreach (explode('; ', $_SERVER['HTTP_COOKIE'] ?? '') as $pair) {
[$name, $value] = explode('=', $pair, 2) + [null, null];
if ($name !== null) $cookies[$name] = urldecode($value);
}
?>
<?php
// 設定過期時間為過去
setcookie('user_id', '', [
'expires' => time() - 3600,
'path' => '/',
'domain' => 'example.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]);
?>
| 屬性 | 用途 | 建議值 |
|---|---|---|
| Secure | 僅 HTTPS 傳輸 | true(生產環境必須) |
| HttpOnly | 阻擋 JavaScript 存取 | true(除非前端需讀取) |
| SameSite | 跨站請求攜帶控制 | Lax(平衡安全與體驗),關鍵操作用 Strict |
| Domain | Cookie 作用域 | 明確指定,避免過寬 |
| Path | 路徑範圍 | /(全站)或特定路徑 |
<?php
// 主域 example.com,子域 a.example.com, b.example.com
// 設定 Domain 為 .example.com(前綴點)或 example.com(現代瀏覽器相容)
setcookie('shared_token', $token, [
'expires' => time() + 86400 * 7,
'path' => '/',
'domain' => 'example.com', // 不加前綴點,現代瀏覽器包含子域
'secure' => true,
'httponly' => true,
'samesite' => 'None' // 跨子域若為不同站點需 None + Secure
]);
?>
<?php
class SecureCookie {
private string $secretKey;
private string $cipher = 'aes-256-gcm';
public function __construct(string $secretKey) {
$this->secretKey = $secretKey;
}
public function set(string $name, mixed $value, array $options = []): void {
$payload = json_encode($value, JSON_UNESCAPED_UNICODE);
$iv = random_bytes(12); // GCM 建議 12 bytes
$tag = '';
$encrypted = openssl_encrypt($payload, $this->cipher, $this->secretKey, OPENSSL_RAW_DATA, $iv, $tag);
$cookieValue = base64_encode($iv . $tag . $encrypted);
setcookie($name, $cookieValue, $options + [
'httponly' => true,
'secure' => true,
'samesite' => 'Lax'
]);
}
public function get(string $name): mixed {
$cookieValue = $_COOKIE[$name] ?? null;
if (!$cookieValue) return null;
$data = base64_decode($cookieValue, true);
if (!$data) return null;
$iv = substr($data, 0, 12);
$tag = substr($data, 12, 16);
$encrypted = substr($data, 28);
$decrypted = openssl_decrypt($encrypted, $this->cipher, $this->secretKey, OPENSSL_RAW_DATA, $iv, $tag);
if ($decrypted === false) return null;
return json_decode($decrypted, true);
}
}
// 使用
$cookie = new SecureCookie($_ENV['COOKIE_SECRET']);
$cookie->set('user_prefs', ['theme' => 'dark', 'lang' => 'zh-TW']);
$prefs = $cookie->get('user_prefs');
?>
| 特性 | Session | Cookie |
|---|---|---|
| 儲存位置 | 伺服器(檔案/Redis/DB) | 客戶端瀏覽器 |
| 資料大小 | 無實際限制 | 單個 4KB,總計約 300 個 |
| 敏感資料 | 適合(不暴露給客戶端) | 不適合(可被竄改/讀取) |
| 跨伺服器 | 需共享儲存(Redis/DB) | 天然跨伺服器 |
| 生命週期 | 瀏覽器關閉失效(可調整) | 可設定長期 |
<?php
// 1. 取得明確同意後才設定非必要 Cookie
if (isset($_COOKIE['cookie_consent']) && $_COOKIE['cookie_consent'] === 'accepted') {
setcookie('analytics_id', generateId(), [...]);
}
// 2. 提供撤銷同意機制
if ($_POST['revoke_consent'] ?? false) {
setcookie('analytics_id', '', ['expires' => time() - 3600, ...]);
setcookie('cookie_consent', 'revoked', ['expires' => time() + 86400 * 365, ...]);
}
// 3. Cookie 政策頁面說明用途、保存期限、第三方共享
?>
正確使用 Cookie 需兼顧功能、安全與法規。核心原則:敏感資料進 Session/資料庫、最小權限原則設定屬性、加密簽名防竄改、遵循隱私法規取得同意。