本文目录导读:

- 绝对禁忌(常见安全漏洞)
- 最安全方案:Session + 高强度配置
- 增强安全:会话指纹验证(防Session劫持)
- 高安全场景:Token + 设备管理(推荐)
- 高级:双重身份验证(可选)
- 登出与清理
- 完整安全清单(检查表)
- 最佳实践建议
在 PHP 中安全地存储登录态,核心原则是:服务端存储状态,客户端只存储不可篡改的凭证,以下是分层级的安全存储方案,从基础到进阶:
绝对禁忌(常见安全漏洞)
// ❌ 错误示例:直接将用户ID存入Cookie
setcookie('user_id', '123', time()+3600);
// ❌ 错误示例:序列化用户信息存入Cookie
setcookie('user_data', serialize($user), time()+3600);
// ❌ 错误示例:明文存储密码在Session
$_SESSION['password'] = $user['password'];
最安全方案:Session + 高强度配置
1 服务端配置(php.ini 或 ini_set)
// 在应用启动时设置
ini_set('session.use_strict_mode', 1); // 严格模式
ini_set('session.use_only_cookies', 1); // 仅用Cookie
ini_set('session.cookie_httponly', 1); // 禁止JS访问
ini_set('session.cookie_secure', 1); // HTTPS下传输
ini_set('session.cookie_samesite', 'Strict'); // 防御CSRF
ini_set('session.gc_maxlifetime', 7200); // 过期时间(秒)
ini_set('session.cookie_lifetime', 0); // 浏览器关闭即失效
2 安全的Session启动代码
class SecureSession {
public static function start(array $options = []) {
// 强制使用安全配置
$defaults = [
'use_strict_mode' => 1,
'use_only_cookies' => 1,
'cookie_httponly' => 1,
'cookie_secure' => isset($_SERVER['HTTPS']),
'cookie_samesite' => 'Strict',
'gc_maxlifetime' => 3600,
'name' => 'SESSION_ID',
'sid_length' => 48, // 128-bit
'sid_bits_per_character' => 6 // 更高安全性
];
$config = array_merge($defaults, $options);
foreach ($config as $key => $value) {
ini_set("session.$key", $value);
}
return session_start();
}
}
// 使用
SecureSession::start();
3 登录后存储(最小必要信息)
public function login(array $user) {
// 防固定攻击 - 重新生成ID
session_regenerate_id(true);
// 只存储必要信息,绝不存密码
$_SESSION['user'] = [
'id' => $user['id'],
'username' => $user['username'],
'role' => $user['role'],
'login_time' => time(),
'login_ip' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT']
];
// 可选的会话指纹
$_SESSION['fingerprint'] = hash('sha256',
$_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']
);
}
增强安全:会话指纹验证(防Session劫持)
public function verifyFingerprint() {
if (!isset($_SESSION['fingerprint'])) {
return false;
}
$current = hash('sha256',
$_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']
);
return hash_equals($_SESSION['fingerprint'], $current);
}
// 每次请求检查
if (!verifyFingerprint()) {
session_destroy();
redirect('/login');
}
高安全场景:Token + 设备管理(推荐)
1 数据库表设计
CREATE TABLE auth_tokens (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
token_hash VARCHAR(64) NOT NULL,
device_type VARCHAR(50),
expires_at DATETIME NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
last_used_at DATETIME,
is_revoked TINYINT(1) DEFAULT 0,
INDEX(user_id),
INDEX(token_hash),
UNIQUE KEY token_unique (token_hash)
);
2 令牌生成与存储
public function createAuthToken(int $userId): array {
// 生成高熵随机令牌
$token = bin2hex(random_bytes(32));
$token_hash = hash('sha256', $token);
$expires = date('Y-m-d H:i:s', time() + 604800); // 7天
// 存入数据库(只存哈希)
DB::insert('auth_tokens', [
'user_id' => $userId,
'token_hash' => $token_hash,
'expires_at' => $expires
]);
// 返回明文给客户端(仅此一次)
return [
'token' => $token,
'expires_at' => $expires
];
}
3 令牌验证
public function verifyToken(string $token): ?array {
$token_hash = hash('sha256', $token);
$row = DB::fetch("SELECT * FROM auth_tokens
WHERE token_hash = ? AND is_revoked = 0 AND expires_at > NOW()",
[$token_hash]);
if (!$row) return null;
// 更新最后使用时间
DB::execute("UPDATE auth_tokens SET last_used_at = NOW() WHERE id = ?",
[$row['id']]);
return $row;
}
4 客户端存储(Cookie)
setcookie(
'auth_token',
$token,
[
'expires' => time() + 604800,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict'
]
);
高级:双重身份验证(可选)
// 高风险操作时要求二次验证
public function require2FA() {
// 生成一次性验证码
$code = random_int(100000, 999999);
// 存入Session(仅60秒有效)
$_SESSION['2fa'] = [
'code' => password_hash($code, PASSWORD_DEFAULT),
'expires' => time() + 60,
'purpose' => 'login'
];
// 发送到用户手机
sendSMSCode($code);
return true;
}
登出与清理
public function logout() {
// 清Session
$_SESSION = [];
// 清除Cookie
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();
// 如果在用Token方案,同时撤销数据库中的Token
revokeToken($_COOKIE['auth_token']);
redirect('/login');
}
完整安全清单(检查表)
| 安全措施 | 实现方式 |
|---|---|
| HTTPS强制 | 所有Cookie加Secure标志 |
| 会话固定防护 | session_regenerate_id(true) |
| 会话劫持防护 | IP+UserAgent指纹 |
| XSS防护 | HttpOnly + 全局转义输出 |
| CSRF防护 | SameSite=Strict + CSRF Token |
| 弱密码防护 | token至少256bit随机数 |
| 最小权限 | 只存必要ID,不存敏感信息 |
| 过期机制 | Session + Token双重过期 |
最佳实践建议
- 生产环境必须HTTPS - 否则一切加密都无效
- 不信任客户端任何输入 - ID、角色等必须从服务端获取
- 定期轮换 - 定期让用户重新登录(如7天)
- 记录日志 - 记录登录IP、时间、设备
- 多因素认证 - 高安全场景启用2FA
核心原则总结:
- ✅ 状态存服务端(Session/DB)
- ✅ 客户端只有不可解析的随机Token
- ✅ 敏感信息绝不进Cookie
- ✅ 所有传输走HTTPS
- ✅ 防XSS、CSRF、Session劫持
推荐使用 Session(简单场景) 或 Token+数据库(多设备/移动端场景),具体依据应用架构选择。