我的web应用程序使用会话存储关于用户的信息,一旦他们登录,并维护这些信息,因为他们在应用程序内从页面到页面。在这个特定的应用程序中,我存储的人的user_id, first_name和last_name。
我想在登录时提供一个“让我登录”选项,在用户的机器上放置一个cookie,为期两周,当他们返回应用程序时,将以相同的细节重新启动他们的会话。
做这件事的最佳方法是什么?我不想在cookie中存储他们的user_id,因为这似乎会让一个用户很容易尝试和伪造另一个用户的身份。
我的web应用程序使用会话存储关于用户的信息,一旦他们登录,并维护这些信息,因为他们在应用程序内从页面到页面。在这个特定的应用程序中,我存储的人的user_id, first_name和last_name。
我想在登录时提供一个“让我登录”选项,在用户的机器上放置一个cookie,为期两周,当他们返回应用程序时,将以相同的细节重新启动他们的会话。
做这件事的最佳方法是什么?我不想在cookie中存储他们的user_id,因为这似乎会让一个用户很容易尝试和伪造另一个用户的身份。
当前回答
我的解是这样的。它不是百分之百的防弹,但我认为它在大多数情况下都能帮你。
当用户成功登录时,创建一个包含以下信息的字符串:
$data = (SALT + ":" + hash(User Agent) + ":" + username
+ ":" + LoginTimestamp + ":"+ SALT)
加密$data,设置类型为HttpOnly,并设置cookie。
当用户回到你的网站时,执行以下步骤:
Get cookie data. Remove dangerous characters inside cookie. Explode it with : character. Check validity. If cookie is older than X days then redirect user to login page. If cookie is not old; Get latest password change time from database. If password is changed after user's last login redirect user to login page. If pass wasn't changed recently; Get user's current browser agent. Check whether (currentUserAgentHash == cookieUserAgentHash). IF agents are same go to next step, else redirect to login page. If all steps passed successfully authorize username.
如果用户登出,请删除此cookie。如果用户重新登录,创建新的cookie。
其他回答
我在这里问了这个问题的一个角度,答案将引导您找到所需的所有基于令牌的超时cookie链接。
基本上,您不会将userId存储在cookie中。您存储了一个一次性令牌(巨大的字符串),用户使用它来拾取旧的登录会话。然后,为了使其真正安全,在进行重大操作(比如更改密码本身)时,需要输入密码。
安全注意:基于确定性数据的MD5哈希的cookie是一个坏主意;最好使用从CSPRNG派生的随机令牌。有关更安全的方法,请参阅ircmaxell对这个问题的回答。
通常我会这样做:
User logs in with 'keep me logged in' Create session Create a cookie called SOMETHING containing: md5(salt+username+ip+salt) and a cookie called somethingElse containing id Store cookie in database User does stuff and leaves ---- User returns, check for somethingElse cookie, if it exists, get the old hash from the database for that user, check of the contents of cookie SOMETHING match with the hash from the database, which should also match with a newly calculated hash (for the ip) thus: cookieHash==databaseHash==md5(salt+username+ip+salt), if they do, goto 2, if they don't goto 1
当然,你可以使用不同的cookie名称等,你也可以改变cookie的内容,只是要确保它不容易创建。例如,你也可以在创建用户时创建user_salt,并将其放在cookie中。
你也可以用sha1代替md5(或者几乎任何算法)
我的解是这样的。它不是百分之百的防弹,但我认为它在大多数情况下都能帮你。
当用户成功登录时,创建一个包含以下信息的字符串:
$data = (SALT + ":" + hash(User Agent) + ":" + username
+ ":" + LoginTimestamp + ":"+ SALT)
加密$data,设置类型为HttpOnly,并设置cookie。
当用户回到你的网站时,执行以下步骤:
Get cookie data. Remove dangerous characters inside cookie. Explode it with : character. Check validity. If cookie is older than X days then redirect user to login page. If cookie is not old; Get latest password change time from database. If password is changed after user's last login redirect user to login page. If pass wasn't changed recently; Get user's current browser agent. Check whether (currentUserAgentHash == cookieUserAgentHash). IF agents are same go to next step, else redirect to login page. If all steps passed successfully authorize username.
如果用户登出,请删除此cookie。如果用户重新登录,创建新的cookie。
生成一个散列,其中可能包含只有您知道的秘密,然后将其存储在您的DB中,以便与用户关联。应该工作得很好。
简介
你的标题“让我登录”-最好的方法让我很难知道从哪里开始,因为如果你正在寻找最好的方法,那么你必须考虑以下几点:
识别 安全
饼干
在常见的浏览器cookie盗窃漏洞和跨站点脚本攻击之间,我们必须接受cookie是不安全的。为了帮助提高安全性,您必须注意php setcookies具有额外的功能,例如
Bool setcookie (string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, Bool $secure = false [, Bool $httponly = false]]]]]])
secure(使用HTTPS连接) httponly(通过XSS攻击减少身份盗窃)
定义
令牌(长度为n的不可预知的随机字符串,例如。/dev/urandom) 参考(长度为n的不可预知的随机字符串,例如。/dev/urandom) 签名(使用HMAC方法生成键控哈希值)
简单的方法
一个简单的解决方案是:
用户使用“记住我”登录 带令牌和签名的登录Cookie 什么时候返回,检查签名 如果签名是ok ..然后在数据库中查找username & token 如果无效..返回登录页面 如果有效,自动登录
上面的案例研究总结了本页上所有的例子,但它们的缺点是
没有办法知道饼干是不是被偷了 攻击者可能是访问敏感操作,如更改密码或数据,如个人和烘焙信息等。 受损害的cookie在cookie生命周期内仍然有效
更好的解决方案
一个更好的解决办法是
用户已登录,并选中“记住我” 生成令牌和签名并存储在cookie中 令牌是随机的,仅对单个身份验证有效 令牌将在每次访问站点时替换 当一个未登录的用户访问网站时,签名、令牌和用户名将被验证 记住我的登录应该有限制的访问,不允许修改密码,个人信息等。
示例代码
// Set privateKey
// This should be saved securely
$key = 'fc4d57ed55a78de1a7b31e711866ef5a2848442349f52cd470008f6d30d47282';
$key = pack("H*", $key); // They key is used in binary form
// Am Using Memecahe as Sample Database
$db = new Memcache();
$db->addserver("127.0.0.1");
try {
// Start Remember Me
$rememberMe = new RememberMe($key);
$rememberMe->setDB($db); // set example database
// Check if remember me is present
if ($data = $rememberMe->auth()) {
printf("Returning User %s\n", $data['user']);
// Limit Acces Level
// Disable Change of password and private information etc
} else {
// Sample user
$user = "baba";
// Do normal login
$rememberMe->remember($user);
printf("New Account %s\n", $user);
}
} catch (Exception $e) {
printf("#Error %s\n", $e->getMessage());
}
类使用
class RememberMe {
private $key = null;
private $db;
function __construct($privatekey) {
$this->key = $privatekey;
}
public function setDB($db) {
$this->db = $db;
}
public function auth() {
// Check if remeber me cookie is present
if (! isset($_COOKIE["auto"]) || empty($_COOKIE["auto"])) {
return false;
}
// Decode cookie value
if (! $cookie = @json_decode($_COOKIE["auto"], true)) {
return false;
}
// Check all parameters
if (! (isset($cookie['user']) || isset($cookie['token']) || isset($cookie['signature']))) {
return false;
}
$var = $cookie['user'] . $cookie['token'];
// Check Signature
if (! $this->verify($var, $cookie['signature'])) {
throw new Exception("Cokies has been tampared with");
}
// Check Database
$info = $this->db->get($cookie['user']);
if (! $info) {
return false; // User must have deleted accout
}
// Check User Data
if (! $info = json_decode($info, true)) {
throw new Exception("User Data corrupted");
}
// Verify Token
if ($info['token'] !== $cookie['token']) {
throw new Exception("System Hijacked or User use another browser");
}
/**
* Important
* To make sure the cookie is always change
* reset the Token information
*/
$this->remember($info['user']);
return $info;
}
public function remember($user) {
$cookie = [
"user" => $user,
"token" => $this->getRand(64),
"signature" => null
];
$cookie['signature'] = $this->hash($cookie['user'] . $cookie['token']);
$encoded = json_encode($cookie);
// Add User to database
$this->db->set($user, $encoded);
/**
* Set Cookies
* In production enviroment Use
* setcookie("auto", $encoded, time() + $expiration, "/~root/",
* "example.com", 1, 1);
*/
setcookie("auto", $encoded); // Sample
}
public function verify($data, $hash) {
$rand = substr($hash, 0, 4);
return $this->hash($data, $rand) === $hash;
}
private function hash($value, $rand = null) {
$rand = $rand === null ? $this->getRand(4) : $rand;
return $rand . bin2hex(hash_hmac('sha256', $value . $rand, $this->key, true));
}
private function getRand($length) {
switch (true) {
case function_exists("mcrypt_create_iv") :
$r = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
break;
case function_exists("openssl_random_pseudo_bytes") :
$r = openssl_random_pseudo_bytes($length);
break;
case is_readable('/dev/urandom') : // deceze
$r = file_get_contents('/dev/urandom', false, null, 0, $length);
break;
default :
$i = 0;
$r = "";
while($i ++ < $length) {
$r .= chr(mt_rand(0, 255));
}
break;
}
return substr(bin2hex($r), 0, $length);
}
}
在Firefox和Chrome中进行测试
优势
更好的安全性 攻击者访问受限 当cookie被盗时,它只对单次访问有效 当下一次原始用户访问该网站时,您可以自动检测并通知用户盗窃
缺点
不支持通过多个浏览器(移动和Web)进行持久连接 cookie仍然可以被窃取,因为用户只有在下次登录后才会收到通知。
快速修复
为每个必须有持久连接的系统引入审批系统 使用多个cookie进行认证
多重Cookie方法
当攻击者要窃取cookie时,唯一的焦点是一个特定的网站或域名。example.com
但实际上,您可以从2个不同的域(example.com & fakeaddsite.com)验证用户,并使其看起来像“广告Cookie”
用户使用“记住我”登录example.com 存储用户名,令牌,在cookie引用 存储用户名,令牌,在数据库引用等。Memcache 通过get和iframe发送引用id到fakeaddsite.com fakeaddsite.com使用引用从数据库获取用户和令牌 Fakeaddsite.com存储签名 当用户从fakeaddsite.com返回带有iframe的签名信息时 结合数据并进行验证 …你知道剩下的
有些人可能会想,你怎么能使用两个不同的cookie呢?这是可能的,想象example.com = localhost和fakeaddsite.com = 192.168.1.120。如果你检查饼干,它看起来是这样的
从上图来看
当前访问的站点是localhost 它还包含从192.168.1.120设置的cookie
192.168.1.120
只接受定义的HTTP_REFERER 只接受来自指定REMOTE_ADDR的连接 没有JavaScript,没有内容,但除了签名信息和从cookie中添加或检索它之外没有任何内容
优势
99%的情况下你都能骗过攻击者 您可以在攻击者第一次尝试时轻松锁定该帐户 与其他方法一样,在下次登录之前就可以阻止攻击
缺点
多个请求到服务器,只是为了一个登录
改进
使用iframe使用ajax完成