PHP中的安全令牌需要Blowfish还记得我的系统吗?

tex*_*ate 4 php hash

我搜索了SO,我知道有很多"记住我"的问题,但我找不到一个专门用于生成安全令牌的Blowfish.

我正在实现一个登录系统,使用基于cookie的"记住我"功能,我读过的所有教程看起来都不安全(例如,根据时间只是一个未加盐的md5哈希)或者看起来过于复杂.

我不是为银行或类似的东西建立网站,而只是想要一个合理的安全级别.

我建议的系统是简单地为用户名创建一个128 char随机字符串,为登录令牌创建第二个128 char字符串.原始字符串将存储在cookie中,未加保证的sha1'd版本将存入数据库中其用户帐户的行中.

我想我甚至可以在每个页面加载时重新生成字符串.

对我来说这提供了不错的安全性(我认为!)因为:

  1. 黑客无法定位特定帐户但知道他们的用户名(他们需要知道128个字符串)
  2. 要伪造登录cookie并获得访问该帐户的人,必须猜测2 x 128个char字符串.
  3. 如果数据库被黑客攻击,那么为128个字符串创建彩虹表就太困难了.
  4. 只有将实际用户名存储在cookie中,我才能安全地记住用户名.

我的问题是:

  1. 这听起来相当安全吗?我应该使用超过128个字符的字符串吗?
  2. 是否值得使用Blowfish而不是sha1?(即我说对,彩虹表太难以制作?)
  3. 如果没有,运行sha1说1000次有什么好处吗?

非常感谢.

irc*_*ell 7

那么,河豚有两个意思.密码(双向加密算法)或Hash(单向散列算法,特别称为bcrypt).但更多的是在一点点.

那么让我们来看看你假定的优势.

  1. 黑客无法通过了解其用户名来定位特定帐户.

    即使他们知道用户名,也没关系.128位随机字符串足够大,你真的不需要担心攻击者猜测它.我们来做一些数学.

    2^128 == 3 * 10^38 possible combinations
    
    Assuming there are 50 million servers on the internet,
    And each server can do 100,000,000,000 guesses per second (to your server)
    
    3e38 / 50,000,000 / 100,000,000,000 == 6 * 10^19 seconds
    
    Which when converted to years is: 1,902,587,519,025
    
    To have a 50% chance of guessing it, the attacker would need to hit 1/2:
    
    Years to 50% chance of guessing: 951,293,759,512
    
    Run Code Online (Sandbox Code Playgroud)

    因此,除非你担心攻击者试图在接下来的万亿年中侵入你的系统,否则128位足够强大......

  2. 伪造登录cookie ...必须猜测2 x 128个char字符串

    我们已经证明猜测1不会发生.所以添加一秒不是必要的(可能不错,但不需要)

  3. 如果数据库被黑客攻击,那么为128个字符串创建彩虹表就太困难了

    是.然而,这不是你应该关注的.你应该关注的是暴力强迫.但更多关于此...

那么,回答你的实际问题:

  1. 这听起来相当安全吗?

    合理,肯定.过于复杂:绝对.有更简单的方法来解决这个问题......

  2. 是否值得使用Blowfish而不是sha1.

    不.Blowfish用于派生(意思是你想要工作证明的地方).在这种情况下,您要生成MAC(机器验证代码).所以我不会使用Blowfish或SHA1.我会用SHA256或SHA512 ......

  3. 运行sha1 1000次是否有优势.

    没有

更好的方法

我推荐的更好的方法是将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);
}
Run Code Online (Sandbox Code Playgroud)

然后,验证:

function rememberMe() {
    $cookie = isset($COOKIE['rememberme']) ? $COOKIE['rememberme'] : '';
    if ($cookie) {
        list ($user, $token, $mac) = explode(':', $cookie);
        if ($mac !== hash_hmac('sha256', $user . ':' . $token, SECRET_KEY)) {
            return false;
        }
        $usertoken = fetchTokenByUserName($user);
        if (timingSafeCompare($usertoken, $token)) {
            logUserIn($user);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,非常重要的SECRET_KEY是加密秘密(由类似/dev/random和/或从高熵输入得到的东西生成).此外,GenerateRandomToken()需要是一个强大的随机源(mt_rand()不够强大.使用库或使用DEV_URANDOM mcrypt)...

而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) {
    // Prevent issues if string length is 0
    $safe .= chr(0);
    $user .= chr(0);

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