- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我的网络应用程序使用 session 来存储用户登录后的信息,并在用户在应用程序内从一个页面到另一个页面时维护这些信息。在这个特定的应用程序中,我存储了此人的 user_id
、first_name
和 last_name
。
我想在登录时提供一个“让我保持登录”选项,它将在用户的计算机上放置一个 cookie 两周,当他们返回应用程序时,它将以相同的详细信息重新启动他们的 session 。
执行此操作的最佳方法是什么?我不想将他们的 user_id
存储在 cookie 中,因为这似乎会让一个用户很容易尝试伪造另一个用户的身份。
最佳答案
好的,让我直截了本地说:如果您为此目的将用户数据或从用户数据派生的任何内容放入 cookie,那么您做错了什么。
那里。我说了。现在我们可以继续讨论实际的答案了。
您问,散列用户数据有什么问题?嗯,它归结为暴露表面和通过默默无闻的安全性。
想象一下你是一个攻击者。您会在 session 中看到为 remember-me 设置的加密 cookie。它有 32 个字符宽。哎呀。那可能是 MD5...
让我们也想象一下,他们知道您使用的算法。例如:
md5(salt+username+ip+salt)
现在,攻击者需要做的就是暴力破解“盐”(这实际上不是盐,但稍后会详细介绍),他现在可以使用任何用户名生成他想要的所有假 token IP地址!但是暴力破解盐很难,对吧?绝对地。但现代 GPU 非常擅长它。除非您在其中使用足够的随机性(使其足够大),否则它会迅速折叠,并随之掉落您城堡的 key 。
简而言之,保护你的唯一东西就是盐,它并没有你想象的那样真正保护你。
等一下!
所有这些都假设攻击者知道算法!如果它是 secret 和令人困惑的,那么你是安全的,对吧? 错误。这种思路有一个名字:Security Through Obscurity,应该绝不依赖它。
更好的方法
更好的方法是永远不要让用户的信息离开服务器,除了 id。
当用户登录时,生成一个大的(128 到 256 位)随机 token 。将其添加到将 token 映射到用户 ID 的数据库表中,然后将其发送到 cookie 中的客户端。
如果攻击者猜到另一个用户的随机 token 怎么办?
好吧,让我们在这里做一些数学运算。我们正在生成一个 128 位随机 token 。这意味着有:
possibilities = 2^128
possibilities = 3.4 * 10^38
现在,为了说明这个数字是多么的大,让我们想象一下互联网上的每台服务器(比如说今天的 50,000,000 台)都试图以每台每秒 1,000,000,000 的速度暴力破解这个数字。实际上,您的服务器会在这样的负载下崩溃,但让我们来解决这个问题。
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);
}
}
}
注意:不要使用 token 或用户和 token 的组合来查找数据库中的记录。始终确保根据用户获取记录,然后使用时间安全比较功能来比较获取的 token 。 More about timing attacks .
现在,SECRET_KEY
是一个密码 secret (由 /dev/urandom
之类的东西生成和/或派生自高熵输入)。此外,GenerateRandomToken()
需要是强随机源(mt_rand()
还不够强。使用库,例如 RandomLib 或 random_compat ,或 mcrypt_create_iv()
与 DEV_URANDOM
)...
hash_equals()
是为了防止timing attacks .如果您使用低于 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;
}
关于php - "Keep Me Logged In"- 最好的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1354999/
我是一名优秀的程序员,十分优秀!