Cookie 是 HTTP 無狀態協議下維持會話狀態的核心機制。本文系統講解 PHP setcookie() 全參數詳解、陣列 Cookie、安全屬性、跨域共享、加密簽名、SameSite 防 CSRF、GDPR 合規等實戰內容。
setcookie() 完整簽名
setcookie(
string $name,
string $value = "",
int $expires_or_options = 0,
string $path = "",
string $domain = "",
bool $secure = false,
bool $httponly = false
): bool
// PHP 7.3+ 支援選項陣列
setcookie(string $name, string $value = "", array $options = []): bool
參數詳細解析
| 參數 | 說明 | 最佳實踐 |
| name | Cookie 名稱 | 項目前綴 + 功能名,如 app_auth_token |
| value | 值(自動 URL 編碼) | 勿存敏感明文,大數據用 session_id 關聯服務端存儲 |
| expires | 過期 Unix 時間戳,0=會話級 | 持久化:time() + 86400*30;刪除:time() - 3600 |
| path | 生效路徑 | 通常 /;子應用隔離用 /app1/ |
| domain | 生效域名 | .example.com 共享子域;留空僅當前域 |
| secure | 僅 HTTPS 傳輸 | 生產環境必須 true |
| httponly | JS 不可讀(防 XSS 竊取) | 除需 JS 讀取外必須 true |
| samesite | 跨站發送策略 (PHP 7.3+) | 'Lax' 平衡體驗安全;'Strict' 高安全;'None' 需 secure=true |
基礎操作範例
<?php
// 1. 簡單會話級 Cookie
setcookie('lang', 'zh-TW');
// 2. 持久化 30 天,全站可用,僅 HTTPS+HTTPOnly
setcookie(
'remember_me',
$token,
[
'expires' => time() + 86400 * 30,
'path' => '/',
'domain' => '.example.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]
);
// 3. 刪除 Cookie(設置過期時間為過去)
setcookie('lang', '', [
'expires' => time() - 3600,
'path' => '/',
'domain' => '.example.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]);
?>
讀取 Cookie
<?php
// 單個讀取
$lang = $_COOKIE['lang'] ?? 'en';
// 批量讀取(調試)
var_dump($_COOKIE);
// 原始 HTTP 標頭(含未解析)
$raw = $_SERVER['HTTP_COOKIE'] ?? '';
?>
陣列 Cookie 實戰
<?php
// 寫入:自動展開為多個 Cookie
setcookie('pref[theme]', 'dark');
setcookie('pref[lang]', 'zh-TW');
setcookie('pref[timezone]', 'Asia/Taipei');
// 讀取:自動重組為陣列
if (isset($_COOKIE['pref'])) {
foreach ($_COOKIE['pref'] as $key => $val) {
echo "$key: $val
";
}
// 輸出: theme: dark, lang: zh-TW, timezone: Asia/Taipei
}
?>
安全加固:加密簽名 Cookie
<?php
const COOKIE_SECRET = 'your-256-bit-secret-key-here'; // 配置文件注入
/**
* 設置加密簽名 Cookie(防篡改、防讀取)
*/
function setSecureCookie(string $name, mixed $value, array $options = []): bool {
$payload = json_encode($value);
$iv = random_bytes(16);
$ciphertext = openssl_encrypt($payload, 'AES-256-GCM', COOKIE_SECRET, OPENSSL_RAW_DATA, $iv, $tag);
$cookieValue = base64_encode($iv . $tag . $ciphertext);
$defaultOptions = [
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
];
return setcookie($name, $cookieValue, array_merge($defaultOptions, $options));
}
/**
* 讀取並驗證解密
*/
function getSecureCookie(string $name): mixed {
$cookieValue = $_COOKIE[$name] ?? null;
if (!$cookieValue) return null;
$data = base64_decode($cookieValue);
if (strlen($data) < 16 + 16) return null; // IV(16) + TAG(16) 最小長度
$iv = substr($data, 0, 16);
$tag = substr($data, 16, 16);
$ciphertext = substr($data, 32);
$plaintext = openssl_decrypt($ciphertext, 'AES-256-GCM', COOKIE_SECRET, OPENSSL_RAW_DATA, $iv, $tag);
if ($plaintext === false) return null; // 驗證失敗(被篡改)
return json_decode($plaintext, true);
}
// 使用
setSecureCookie('user_profile', ['uid' => 123, 'role' => 'admin']);
$profile = getSecureCookie('user_profile');
?>
跨域/子域共享配置
<?php
// 主域 example.com,子域 api.example.com、app.example.com 共享
setcookie('ssid', $sessionId, [
'expires' => time() + 3600,
'path' => '/',
'domain' => '.example.com', // 關鍵:前綴點號
'secure' => true,
'httponly' => true,
'samesite' => 'None' // 跨域需 None + Secure
]);
// Nginx 代理需轉發 Cookie 標頭
// proxy_pass_header Set-Cookie;
?>
SameSite 屬性深度解析
| 值 | 行為 | 適用場景 |
| Strict | 完全禁止跨站發送(含鏈接跳轉) | 高安全操作:轉帳、刪除、修改密碼 |
| Lax | 允許安全跨站請求(GET 鏈接、表單 POST 導航) | 大多數認證 Cookie(推薦默認) |
| None | 允許所有跨站請求,**必須配合 Secure=true** | 嵌入式 Widget、跨域 API、SSO |
GDPR/隱私合規檢查清單
- [ ] 必要性原則:僅存儲業務必需數據
- [ ] 最小化原則:不存儲身份證、銀行卡等敏感信息
- [ ] 用戶同意:非必要 Cookie 需顯式同意(Cookie Banner)
- [ ] 透明度:隱私政策列明 Cookie 用途、有效期、共享方
- [ ] 撤銷權:提供「拒絕/清除 Cookie」功能
- [ ] 數據保護:傳輸加密、存儲加密、訪問控制
調試技巧
<?php
// 瀏覽器開發者工具 → Application → Cookies
// 命令行 curl 測試
curl -v -b "name=value" https://example.com/
curl -v -c cookies.txt https://example.com/login
curl -v -b cookies.txt https://example.com/api
// PHP 端查看原始標頭
var_dump($_SERVER['HTTP_COOKIE']);
?>
總結
Cookie 操作核心原則:安全屬性全開、最小權限原則、敏感數據不入 Cookie、加密簽名防篡改、SameSite 防 CSRF、合規隱私保護。PHP 7.3+ 選項陣列語法使配置更清晰,建議封裝 setSecureCookie()/getSecureCookie() 統一管理。
- 上一篇文章: PHP foreach 進階陷阱與最佳實踐:引用殘留、拷貝語義、未定義變量防護
- 下一篇文章: PHP 內存管理核心:引用計數與寫時複製(Copy-on-Write)深度解析
Copyright ©
程式碼之家 All Rights Reserved