本文目录导读:

设计 PHP 项目中的 Cookie 过期规则,核心是平衡安全性、用户体验和业务需求,以下是一套从基础到高级的完整设计指南。
核心概念:Cookie 的两种过期机制
- 会话 Cookie (Session Cookie):不设置 Expires 或 Max-Age 属性,浏览器关闭后自动删除。
- 持久 Cookie (Persistent Cookie):设置了
Expires或Max-Age,在指定时间后删除。
// 设置会话 Cookie (浏览器关闭即失效)
setcookie('name', 'value');
// 设置持久 Cookie (30天后失效)
setcookie('name', 'value', time() + 86400 * 30);
// 或使用 Max-Age (更推荐, 相对时间)
setcookie('name', 'value', ['expires' => time() + 86400 * 30, 'samesite' => 'Lax']);
// 注意: PHP 7.3+ 推荐使用 options 数组形式
设计原则与通用规则
- 最小必要原则:Cookie 只存标识符(如 Session ID、Token),不存敏感数据(密码、证件号)。
- 安全优先:必须设置
HttpOnly、Secure、SameSite属性。 - 区分业务场景:登录态、购物车、偏好设置、7天免登录等,过期时间应不同。
常见场景的过期规则设计
用户登录态(最核心)
-
短期会话:会话 Cookie + Session 机制(默认)。
- 优点:最安全,用户关闭浏览器即退出。
- 缺点:体验差,频繁登录。
- 代码:
session_start();(PHP 默认使用会话 Cookie)。
-
长期免登录(记住我):持久 Cookie + Token 刷新机制。
- 设计要点:
- 使用随机 Token(非用户密码)存储在 Cookie 和数据库中。
- 过期时间:建议 7-30天,一般不超过 30 天。
- 刷新规则:每次有效请求时,重新生成 Token 并更新 Cookie 和数据库,防止固定 Token 被窃取后永久有效。
- 安全处理:Cookie 中的 Token 应该被哈希后再存入数据库(不要存明文)。
- 代码示例:
- 设计要点:
// 1. 用户登录时勾选“记住我”
if ($rememberMe) {
$token = bin2hex(random_bytes(32)); // 生成随机 Token
$expires = time() + 86400 * 7; // 7天后过期
// 存储到数据库 (用户ID, 哈希后的Token, 过期时间)
$hashedToken = hash('sha256', $token);
saveTokenToDatabase($userId, $hashedToken, $expires);
// 设置 Cookie
setcookie('remember_token', $token, [
'expires' => $expires,
'path' => '/',
'domain' => 'example.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]);
}
// 2. 用户下次访问时验证
if (!isset($_SESSION['user_id']) && isset($_COOKIE['remember_token'])) {
$token = $_COOKIE['remember_token'];
$hashedToken = hash('sha256', $token);
// 从数据库查找匹配且未过期的 Token
$userData = findTokenInDatabase($hashedToken);
if ($userData && $userData['expires'] > time()) {
// 重新登录会话
$_SESSION['user_id'] = $userData['user_id'];
// **关键: 刷新 Token** (防止固定 Token 泄露)
rotateRememberMeToken($userData['user_id']);
} else {
// Token 无效或过期, 删除 Cookie
setcookie('remember_token', '', time() - 3600);
}
}
购物车(未登录状态)
- 设计:持久 Cookie,过期时间灵活。
- 推荐时间:3-14天(避免用户在选商品过程中购物车丢失)。
- 注意:购物车数据量可能很大,Cookie 有大小限制(4KB),可以用 Session 或数据库存储,Cookie 只存一个 Session ID。
// 存储购物车数据到 Session, 浏览器关闭即消失不合适
// 方案: 使用独立的购物车 Session ID 并设置持久 Cookie
$cartSessionId = session_create_id();
$expires = time() + 86400 * 7;
setcookie('cart_session_id', $cartSessionId, $expires, '/', '', true, true);
用户偏好设置(主题、语言、字体大小)
- 设计:持久 Cookie,过期时间非常长(365天或更长)。
- 安全性:不敏感,可以
HttpOnly = false(前端 JS 可读取,用于动态换肤),但建议还是HttpOnly=true并通过 API 读取。 - 示例:
setcookie('theme', 'dark', [
'expires' => time() + 86400 * 365, // 一年
'path' => '/',
'samesite' => 'Lax'
]);
跟踪与分析(如 Google Analytics 的 _ga)
- 设计:持久 Cookie,1-2 年。
- 注意:需符合 GDPR/中国个人信息保护法,用户需同意。
高级设计:Token 轮换与吊销机制
这是防止 Cookie 被窃取后滥用(持久登录令牌)的关键。
- 轮换机制:
- 每次使用“记住我” Token 成功验证后,立即生成新 Token(旧 Token 失效)。
- 这样即使黑客窃取了某个 Token,用户下次正常登录后,旧 Token 自动失效。
- 版本化:
- 数据库 Token 记录增加
version字段,每次轮换版本号 +1,服务器只认最新版本号的 Token。
- 数据库 Token 记录增加
- 吊销机制:
- 用户修改密码、注销其他设备、账户被锁定等操作时,删除/作废该用户所有遗忘 Token。
- 需要数据库支持:
DELETE FROM user_tokens WHERE user_id = ?。
过期时间的安全考量
- 敏感操作:支付、修改密码、绑定手机等操作不能只靠 Cookie 登录态,必须再次验证密码或 2FA。
- 会话超时:
- 空闲超时:用户长时间无操作(如 30 分钟),自动销毁 Session(短 Token)。
- 绝对超时:用户从登录开始,最多会话 8 小时或 24 小时,强制重新登录(适用于银行、后台管理系统)。
- 实现:在 Session 中存储
last_activity和login_time,每次请求时检查。
// 会话空闲超时
if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > 1800)) {
session_destroy();
session_start();
// 重定向到登录页
}
$_SESSION['last_activity'] = time();
// 会话绝对超时
if (isset($_SESSION['login_time']) && (time() - $_SESSION['login_time'] > 28800)) { // 8小时
// 强制注销
}
完整的设置示例(现代 PHP 7.3+)
// 设置一个安全的长久登录 Cookie
$cookieName = 'auth_token';
$cookieValue = bin2hex(random_bytes(32));
$expires = time() + 86400 * 14; // 14 天后过期
setcookie($cookieName, $cookieValue, [
'expires' => $expires,
'path' => '/',
'domain' => 'example.com', // 匹配你的域名
'secure' => true, // 仅 HTTPS
'httponly' => true, // 禁止 JavaScript 访问
'samesite' => 'Lax' // 防止 CSRF, Lax 是较好的平衡
]);
// 删除 Cookie
setcookie($cookieName, '', [
'expires' => time() - 3600,
'path' => '/',
'domain' => 'example.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]);
最佳实践一览表
| 场景 | 类型 | 过期时间 | 安全属性 | 备注 |
|---|---|---|---|---|
| 登录会话 | Session | 浏览器关闭 | HttpOnly, Secure, SameSite |
默认行为,最安全 |
| 记住我 | Persistent | 7-30 天 | HttpOnly, Secure, SameSite,Token 轮换 |
必须结合数据库 Token 存储和轮换 |
| 购物车 | Persistent | 3-14 天 | HttpOnly, Secure |
最好用 Session ID 控制,数据存服务端 |
| 偏好设置 | Persistent | 1 年 | Secure, SameSite (可 HttpOnly=false 给前端使用) |
安全性低,可灵活处理 |
| 分析跟踪 | Persistent | 1-2 年 | Secure, SameSite |
需用户同意 |
| 敏感操作令牌 | 一次性 | 5-15 分钟 | HttpOnly, Secure, SameSite=Strict |
用于支付、密码重置确认等,用完立即删除 |
| CSRF Token | Session | 浏览器关闭 | SameSite=Strict |
放在表单隐藏字段,不通过 Cookie |
最后建议:不要完全依赖 Cookie 过期时间来保证安全,配合服务端 Session 管理、IP 变化检测、异常登录行为报警等,构建多层防御体系。