嗯,河豚可能意味着两件事。 Cipher(双向加密算法)或Hash(单向哈希算法,具体称为bcrypt)。但稍后会详细介绍。
那么让我们看看你假设的优势。
-
黑客无法通过知道用户名来锁定特定帐户。
即使他们知道用户名,也没关系。 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
因此,除非您担心攻击者会在未来数万亿年内试图侵入您的系统,否则 128 位足够足够强大...
-
要伪造登录 cookie ...必须猜测 2 x 128 字符字符串
我们已经证明猜测 1 不会发生。所以加一秒没必要(可能还不错,但没必要)
-
如果数据库被黑客入侵,为 128 个字符字符串创建彩虹表太难了
是的。然而,这不是你应该关心的。你应该关心的是暴力破解。但稍后会详细介绍...
所以,回答你的实际问题:
-
这听起来是否相当安全。
合理,当然。过于复杂:绝对。有更简单的方法来处理这个......
-
是否值得使用 Blowfish 而不是 sha1。
没有。 Blowfish 用于推导(意味着您想要工作证明的地方)。在这种情况下,您要生成 MAC(机器验证码)。所以我不会使用 Blowfish 或 SHA1。我会使用 SHA256 或 SHA512...
-
运行 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);
}
然后,验证:
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);
}
}
}
现在,SECRET_KEY 是一个密码机密(由 /dev/random 之类的东西生成和/或从高熵输入派生)是非常重要的。此外,GenerateRandomToken() 需要是强随机源(mt_rand() 还不够强。使用库或带 DEV_URANDOM 的 mcrypt)...
而timingSafeCompare是为了防止timing attacks。像这样的:
/**
* 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;
}