PHP登录系统:记住我(持久性cookie)

Kar*_*rem 40 php authentication cookies remember-me

我想在登录前添加"记住我"复选框选项.

在用户的浏览器中安全存储cookie的最佳方法是什么?

例如,Facebook有"记住我"复选框,这样每次进入facebook.com时您都已登录.

我当前的登录使用简单的会话.

Sco*_*ski 45

更新(2017年8月13日):要理解为什么我们分开selectortoken,而不是仅仅使用token,请阅读这篇文章关于拆分令牌,以防止计时攻击的SELECT查询.

我将提取本博客文章中概述的关于安全长期身份验证的策略,因为它涵盖了很多方面,我们只对"记住我"部分感兴趣.

序言 - 数据库结构

我们想要一个来自用户表的单独表,看起来像这样(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`)
);
Run Code Online (Sandbox Code Playgroud)

这里重要的事情是,selector并且token是单独的领域.

登录后

如果你没有random_bytes(),只需抓取random_compat的副本.

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)
        ]
    );
}
Run Code Online (Sandbox Code Playgroud)

重新验证页面加载

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
    }
}
Run Code Online (Sandbox Code Playgroud)

细节

我们为选择器使用9个字节的随机数据(base64编码为12个字符).这提供了72位密钥空间,因此提供了2 36位的抗冲突性(生日攻击),这比我们的存储容量(integer(11) UNSIGNED)大16倍.

我们使用33字节(264位)的随机性作为我们的实际验证器.在所有实际情况中,这应该是不可预测的.

我们在数据库中存储了身份验证器的SHA256哈希值.这减轻了信息泄露后用户冒充的风险.

我们重新计算存储在用户cookie中的authenticator值的SHA256哈希值,然后将其与存储的SHA256哈希值进行比较,hash_equals()以防止时间攻击.

我们将选择器与验证器分开,因为DB查找不是常量时间.这消除了定时泄漏对搜索的潜在影响,而不会导致严重的性能损失.

  • 优秀作品。是否值得在您的代码中添加到期检查?您是否在代码中的其他地方重新生成令牌(例如,每 10 个页面加载中就有 1 个)? (2认同)
  • @AgainMe 可以安全抵御任何合理的威胁模型。如果有人可以拦截/克隆 cookie(恶意软件),则服务器无法或不应该对这种风险采取任何措施。另请参阅:https://paragonie.com/blog/2016/03/client-authenticity-is-not-server-s-problem (2认同)

Pau*_*xon 36

这个问题被问了很多,这里有一些链接给你.

在这个问题的答案中还收集了一些很好的资源:网站认证的权威指南

  • 更好的解决方案:[在具有长期持久性的PHP应用程序中实现安全用户身份验证](https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence#title.2) (2015) (2认同)

归档时间:

查看次数:

36983 次

最近记录:

8 年,5 月 前