- 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/
我想了解 Ruby 方法 methods() 是如何工作的。 我尝试使用“ruby 方法”在 Google 上搜索,但这不是我需要的。 我也看过 ruby-doc.org,但我没有找到这种方法。
Test 方法 对指定的字符串执行一个正则表达式搜索,并返回一个 Boolean 值指示是否找到匹配的模式。 object.Test(string) 参数 object 必选项。总是一个
Replace 方法 替换在正则表达式查找中找到的文本。 object.Replace(string1, string2) 参数 object 必选项。总是一个 RegExp 对象的名称。
Raise 方法 生成运行时错误 object.Raise(number, source, description, helpfile, helpcontext) 参数 object 应为
Execute 方法 对指定的字符串执行正则表达式搜索。 object.Execute(string) 参数 object 必选项。总是一个 RegExp 对象的名称。 string
Clear 方法 清除 Err 对象的所有属性设置。 object.Clear object 应为 Err 对象的名称。 说明 在错误处理后,使用 Clear 显式地清除 Err 对象。此
CopyFile 方法 将一个或多个文件从某位置复制到另一位置。 object.CopyFile source, destination[, overwrite] 参数 object 必选
Copy 方法 将指定的文件或文件夹从某位置复制到另一位置。 object.Copy destination[, overwrite] 参数 object 必选项。应为 File 或 F
Close 方法 关闭打开的 TextStream 文件。 object.Close object 应为 TextStream 对象的名称。 说明 下面例子举例说明如何使用 Close 方
BuildPath 方法 向现有路径后添加名称。 object.BuildPath(path, name) 参数 object 必选项。应为 FileSystemObject 对象的名称
GetFolder 方法 返回与指定的路径中某文件夹相应的 Folder 对象。 object.GetFolder(folderspec) 参数 object 必选项。应为 FileSy
GetFileName 方法 返回指定路径(不是指定驱动器路径部分)的最后一个文件或文件夹。 object.GetFileName(pathspec) 参数 object 必选项。应为
GetFile 方法 返回与指定路径中某文件相应的 File 对象。 object.GetFile(filespec) 参数 object 必选项。应为 FileSystemObject
GetExtensionName 方法 返回字符串,该字符串包含路径最后一个组成部分的扩展名。 object.GetExtensionName(path) 参数 object 必选项。应
GetDriveName 方法 返回包含指定路径中驱动器名的字符串。 object.GetDriveName(path) 参数 object 必选项。应为 FileSystemObjec
GetDrive 方法 返回与指定的路径中驱动器相对应的 Drive 对象。 object.GetDrive drivespec 参数 object 必选项。应为 FileSystemO
GetBaseName 方法 返回字符串,其中包含文件的基本名 (不带扩展名), 或者提供的路径说明中的文件夹。 object.GetBaseName(path) 参数 object 必
GetAbsolutePathName 方法 从提供的指定路径中返回完整且含义明确的路径。 object.GetAbsolutePathName(pathspec) 参数 object
FolderExists 方法 如果指定的文件夹存在,则返回 True;否则返回 False。 object.FolderExists(folderspec) 参数 object 必选项
FileExists 方法 如果指定的文件存在返回 True;否则返回 False。 object.FileExists(filespec) 参数 object 必选项。应为 FileS
我是一名优秀的程序员,十分优秀!