PHP 登录系统:记住我(持久性 Cookie)前言 - 数据库结构登录后页面加载时重新进行身份验证详
2022-08-30 10:53:44
我想在登录之前添加一个“记住我”复选框选项。
在用户浏览器中安全存储 Cookie 的最佳方法是什么?
例如,Facebook有他们的“记住我”复选框,以便每次您输入 facebook.com 您已经登录。
我当前的登录名使用简单会话。
我想在登录之前添加一个“记住我”复选框选项。
在用户浏览器中安全存储 Cookie 的最佳方法是什么?
例如,Facebook有他们的“记住我”复选框,以便每次您输入 facebook.com 您已经登录。
我当前的登录名使用简单会话。
更新 (2017-08-13):要了解我们为什么要分离,而不仅仅是使用 ,请阅读这篇关于拆分令牌以防止对 SELECT 查询进行定时攻击的文章。
selectortokentoken
我将提取这篇博客文章中概述的关于安全长期身份验证的策略,因为它涵盖了很多领域,我们只对“记住我”部分感兴趣。
我们想要一个与用户表分开的表,如下所示(MySQL):
CREATE TABLE `auth_tokens` (
`id` integer(11) not null UNSIGNED AUTO_INCREMENT,
`selector` char(12),
`token` char(64),
`userid` integer(11) not null UNSIGNED,
`expires` datetime,
PRIMARY KEY (`id`)
);
这里重要的事情是,并且是单独的字段。selectortoken
如果您没有 ,只需获取random_compat的副本即可。random_bytes()
if ($login->success && $login->rememberMe) { // However you implement it
$selector = base64_encode(random_bytes(9));
$authenticator = random_bytes(33);
setcookie(
'remember',
$selector.':'.base64_encode($authenticator),
time() + 864000,
'/',
'yourdomain.com',
true, // TLS-only
true // http-only
);
$database->exec(
"INSERT INTO auth_tokens (selector, token, userid, expires) VALUES (?, ?, ?, ?)",
[
$selector,
hash('sha256', $authenticator),
$login->userId,
date('Y-m-d\TH:i:s', time() + 864000)
]
);
}
if (empty($_SESSION['userid']) && !empty($_COOKIE['remember'])) {
list($selector, $authenticator) = explode(':', $_COOKIE['remember']);
$row = $database->selectRow(
"SELECT * FROM auth_tokens WHERE selector = ?",
[
$selector
]
);
if (hash_equals($row['token'], hash('sha256', base64_decode($authenticator)))) {
$_SESSION['userid'] = $row['userid'];
// Then regenerate login token as above
}
}
我们使用 9 个字节的随机数据(base64 编码为 12 个字符)作为选择器。这提供了72位的密钥空间,因此提供了236位的抗碰撞性(生日攻击),这比我们的存储容量()大16倍。integer(11) UNSIGNED
我们使用33字节(264位)的随机性作为实际身份验证器。在所有实际情况下,这应该是不可预测的。
我们将身份验证器的 SHA256 哈希存储在数据库中。这降低了信息泄露后用户冒充的风险。
我们重新计算存储在用户cookie中的身份验证器值的SHA256哈希,然后使用hash_equals()将其与存储的SHA256哈希进行比较,以防止定时攻击。
我们将选择器与身份验证器分开,因为数据库查找不是常量时间。这消除了时序泄漏对搜索的潜在影响,而不会造成严重的性能损失。