当前位置: 首页 > 面试题库 >

“保持登录状态”-最佳方法

百里阳平
2023-03-14
问题内容

我的Web应用程序使用会话来存储有关用户登录后的信息,并在用户在应用程序中逐页浏览时维护该信息。在这个特定的应用程序,我存储user_idfirst_namelast_name人的。

我想在登录时提供“保持登录状态”选项,该选项会将cookie在用户的计算机上放置两周,当他们返回应用程序时,将使用相同的详细信息重新开始会话。

这样做的最佳方法是什么?我不想将其存储user_id在cookie中,因为这似乎会使一个用户更容易伪造另一位用户的身份。


问题答案:

好的,让我直言不讳:如果您为此目的将用户数据或从用户数据派生的任何内容放入cookie,则表示您做错了。

那里。我说了。现在我们可以继续实际的答案了。

您问哈希用户数据有什么问题吗?好吧,它可归结为暴露表面和安全性。

想象一下您是攻击者。您会在会话中看到为“记住我”设置的加密cookie。宽度为32个字符。啧啧。那可能是MD5 …

我们还要想象一下,他们知道您使用的算法。例如:

md5(salt+username+ip+salt)

现在,攻击者所需要做的就是强行加“盐”(这实际上不是盐,但稍后会更多),他现在可以使用其IP地址的任何用户名生成他想要的所有伪造令牌!但是强行撒盐很难,对吗?绝对。但是现代的GPU非常擅长于此。并且除非您在其中使用足够的随机性(使其足够大),否则它将很快下降,并随即成为城堡的关键。

简而言之,唯一保护您的是盐,它并没有像您想的那样真正保护您。

可是等等!

所有这些都假定攻击者知道该算法!如果这是秘密且令人困惑,那么您就安全了,对吗? 错误 。这种思路有一个名字:“ 通过模糊实现安全”
永远不要 依赖它。

更好的方法

更好的方法是永远不要让用户的信息离开服务器,除了ID。

用户登录时,生成一个大的(128至256位)随机令牌。将其添加到将令牌映射到用户标识的数据库表中,然后将其发送到cookie中的客户端。

如果攻击者猜测另一个用户的随机令牌怎么办?

好吧,让我们在这里做一些数学运算。我们正在生成一个128位随机令牌。这意味着有:

possibilities = 2^128
possibilities = 3.4 * 10^38

现在,为了展示这个数字有多么荒谬,让我们想象一下互联网上的每台服务器(今天的数字是5000万)试图以每秒10亿的速度暴力破解该数字。实际上,您的服务器会在这样的负载下融化,但让我们来解决这个问题。

guesses_per_second = servers * guesses
guesses_per_second = 50,000,000 * 1,000,000,000
guesses_per_second = 50,000,000,000,000,000

因此,每秒50万亿次猜测。快!对?

time_to_guess = possibilities / guesses_per_second
time_to_guess = 3.4e38 / 50,000,000,000,000,000
time_to_guess = 6,800,000,000,000,000,000,000

6.8秒

让我们尝试将其归结为更友好的数字。

215,626,585,489,599 years

甚至更好:

47917 times the age of the universe

是的,这是宇宙年龄的47917倍…

基本上,它不会被破解。

所以总结一下:

我建议的更好的方法是将cookie分为三个部分存储。

function onLogin($user) {
    $token = GenerateRandomToken(); // generate a token, should be 128 - 256 bit
    storeTokenForUser($user, $token);
    $cookie = $user . ':' . $token;
    $mac = hash_hmac('sha256', $cookie, SECRET_KEY);
    $cookie .= ':' . $mac;
    setcookie('rememberme', $cookie);
}

然后,进行验证:

function rememberMe() {
    $cookie = isset($_COOKIE['rememberme']) ? $_COOKIE['rememberme'] : '';
    if ($cookie) {
        list ($user, $token, $mac) = explode(':', $cookie);
        if (!hash_equals(hash_hmac('sha256', $user . ':' . $token, SECRET_KEY), $mac)) {
            return false;
        }
        $usertoken = fetchTokenByUserName($user);
        if (hash_equals($usertoken, $token)) {
            logUserIn($user);
        }
    }
}

注意:不要使用令牌或用户和令牌的组合来查找数据库中的记录。始终确保根据用户来获取记录,并使用时序安全比较功能随后对获取的令牌进行比较。有关定时攻击的更多信息。

现在, 非常
重要的一点是SECRET_KEY成为一个密码秘密(由类似东西的东西生成/dev/urandom和/或从高熵输入中得出)。另外,还GenerateRandomToken()需要成为一个强大的随机源(mt_rand()还不够强大。请使用一个库,例如RandomLib或random_compat,或mcrypt_create_iv()与一起使用DEV_URANDOM)…

hash_equals()是为了防止定时攻击。如果使用PHP
5.6以下的PHP版本,hash_equals()则不支持该功能。在这种情况下,您可以替换hash_equals()为timingSafeCompare函数:

/**
 * A timing safe equals comparison
 *
 * To prevent leaking length information, it is important
 * that user input is always used as the second parameter.
 *
 * @param string $safe The internal (safe) value to be checked
 * @param string $user The user submitted (unsafe) value
 *
 * @return boolean True if the two strings are identical.
 */
function timingSafeCompare($safe, $user) {
    if (function_exists('hash_equals')) {
        return hash_equals($safe, $user); // PHP 5.6
    }
    // Prevent issues if string length is 0
    $safe .= chr(0);
    $user .= chr(0);

    // mbstring.func_overload can make strlen() return invalid numbers
    // when operating on raw binary strings; force an 8bit charset here:
    if (function_exists('mb_strlen')) {
        $safeLen = mb_strlen($safe, '8bit');
        $userLen = mb_strlen($user, '8bit');
    } else {
        $safeLen = strlen($safe);
        $userLen = strlen($user);
    }

    // Set the result to the difference between the lengths
    $result = $safeLen - $userLen;

    // Note that we ALWAYS iterate over the user-supplied length
    // This is to prevent leaking length information
    for ($i = 0; $i < $userLen; $i++) {
        // Using % here is a trick to prevent notices
        // It's safe, since if the lengths are different
        // $result is already non-0
        $result |= (ord($safe[$i % $safeLen]) ^ ord($user[$i]));
    }

    // They are only identical strings if $result is exactly 0...
    return $result === 0;
}


 类似资料:
  • 本文向大家介绍微信小程序 如何保持登录状态,包括了微信小程序 如何保持登录状态的使用技巧和注意事项,需要的朋友参考一下 问题 由于wx.request()发起的每次请求对于服务器来说都是不同的会话(wx.request()请求是先经过微信服务器再到达我们的服务器),这样会导致后续请求都相当于未登录的状态。 解决方案 将登陆时后端返回的session保存在本地, 然后将session存放在cooki

  • 本文向大家介绍利用JSP session对象保持住登录状态,包括了利用JSP session对象保持住登录状态的使用技巧和注意事项,需要的朋友参考一下 http协议本身是一种无状态的协议,也就是客户端连续发送的多个请求之间没有联系,下一次请求不关心上一次请求的状态。 而实际运用中却希望服务器能记住客户端请求的状态,比如在网上购物系统中,服务器端应该能够识别并跟踪每个登录到系统中的用户挑选并购买商品

  • 问题内容: 我目前在tensorflow中具有一系列链接在一​​起的RNN的以下代码。我不使用MultiRNN,因为稍后我将对每个图层的输出进行处理。 目前,我有固定的时间步数。但是,我想将其更改为只有一个时间步长,但要记住批次之间的状态。因此,我需要为每个层创建一个状态变量,并将其分配给每个层的最终状态。这样的事情。 然后,对于每一层,我都需要评估sess.run函数中的保存状态以及调用训练函数

  • 本文翻译自 developers.google.com 作者:Philip Walton 原文链接:Best Practices for Persisting Application State with IndexedDB 当网站或应用首次被加载时,首先通常需要准备大量工作去构造初始的应用状态信息,然后再使用这些信息去渲染界面。例如,有的应用需要用户进行身份验证,之后才能去发一些 API 请求获

  • HTTP协议是无状态的:每次请求都是一次新的请求,不会记得之前通信的状态 客户端与服务器端的一次通信,就是一次会话 实现状态保持的方式:在客户端或服务器端存储与会话有关的数据 存储方式包括cookie、session,会话一般指session对象 使用cookie,所有数据存储在客户端,注意不要存储敏感信息 推荐使用sesison方式,所有数据存储在服务器端,在客户端cookie中存储sessio

  • 我正在创建一个Ionic应用程序(iOS和带有电容的Android)。我在我的应用程序中添加了Firebase身份验证,但每次退出应用程序时,我都需要重新登录。 我看到了这个,但是没有太多的文档,并且我尝试了它但是没有工作。我不确定我正确地使用了它,或者在正确的地方添加了代码。https://firebase.google.com/docs/auth/web/manage-users?HL=en