我目前正在为网络项目实施“记住我”系统。在阅读了几篇文章之后(虽然不是那么有用的文章)“我想出了这个系统,以防止cookie被用来访问除被分配给的用户以外的帐户:
如果可以提出任何问题/改进,请指出它我总是受到批评:)
答案 0 :(得分:3)
你的系统有一个良好的开端,但仍然非常容易被劫持(如果有人接受存储在cookie中的哈希,他可以轻松接管会话。)
无论如何,这里有一些关于同一主题的好帖子:
我认为你应该从阅读这些链接开始。
确实没有一个完整的万无一失的系统(检查第3和第4个链接,它们提供了一些有趣的额外的东西;用户代理也是你可以使用的某种标识符)。我建议你使用某种OpenID提供程序(或多个)来获取所有安全性的东西。如果你想自己做这件事:你必须权衡专业人士和骗子以达到最大安全性或少一点。
答案 1 :(得分:2)
像其他人一样说你有一个好的开始。我建议创建一个存储一些cookie信息的表,特别是:
user_id
random_hash
ip_address
date_created
然后当用户登录时,您生成一个像您所说的哈希并将该数据插入表中并创建存储其user_id&的cookie。哈希。
当用户返回时,检查hash和ip_address并查找匹配项。如果找到一个,请验证所述user_id AND 删除已使用的哈希值,并为下次登录生成一个新哈希值。我相信你可以看到这样做的好处。
此外 - 确保您使用SSL和(重要)绝不允许用户在不提供密码的情况下更改或查看任何关键信息!
干杯!
答案 2 :(得分:1)
不,这太复杂了。
使用列remember_me
user_id | hash
(或您喜欢的任何表名称)表
在用“记住我”选项对用户进行身份验证后,生成随机哈希,并将其放入与对应的user_id和用户的cookie中的表中
当用户访问某个页面并且未经过身份验证且具有“remember_me”Cookie时 - 按Cookie值查找user_id
重要的是:没有正当理由依赖您的remember_me客户数据真实用户的姓名,电子邮件,密码或其他任何东西
PS:如果您愿意,可以使用user_id值附加客户端数据
答案 3 :(得分:0)
是的,听起来不错,但为什么重新发明轮子?您正在描述会话。您也可以使用PHP内置的功能。
答案 4 :(得分:0)
我想我找到了一个聪明的解决方案!
此(复杂?)脚本的优点:
当用户在选中“记住我”的情况下成功登录时,除了标准会话管理cookie外,还会发出登录cookie。[2]
登录cookie包含用户的用户名,系列标识符和令牌。系列和令牌是来自适当大空间的不可猜测的随机数。这三个都存储在数据库表中。
当非登录用户访问该网站并显示登录cookie时,将在数据库中查找用户名,系列和令牌。
如果存在三元组,则认为用户已通过身份验证。使用的令牌将从数据库中删除。会生成一个新令牌,并使用用户名和相同的系列标识符存储在数据库中,并向用户颁发一个包含这三个令牌的新登录cookie。
如果存在用户名和系列,但令牌不匹配,则认为是盗窃。用户收到措辞强烈的警告,并删除了用户记住的所有会话。
如果不存在用户名和系列,则登录cookie将被忽略。
我已经在数据库中创建了一个表,其中包含以下信息:
session | token | username | expire
“记住我” cookie将具有以下设置:
$value = "$session|$token|$userhash"; //Total length = 106
Session
将是40个(sha1)字符的字符串。Token
将是32个(md5)字符的字符串。Userhash
将是一个由32个字符(用户名md5)组成的字符串。Username
将是普通用户名。Expire
现在将是60天。脚本:
ini_set('session.hash_function', 'sha1');
ini_set('session.hash_bits_per_character', '4');
session_start();
if(isset($_POST['user']) && isset($_POST['password'])) {
if(isset($_COOKIE['remember']) && strlen($_COOKIE['remember']) == 106) {
//THERE is a cookie, which is the right length 40session+32token+32user+2'|'
//Now lets go check it...
//How do I protect this script form harmful user input?
$plode = explode('|', $_COOKIE['remember']);
$session = htmlspecialchars($plode[0]);
$token = htmlspecialchars($plode[1]);
$userhash = htmlspecialchars($plode[2]);
$result_query = $auth->query("SELECT user
FROM sessions
WHERE session = '$session'
AND token = '$token'
AND user = '$userhash'");
$result_array = array();
$auth_query = $auth->query("SELECT user FROM sessions WHERE session = '$session' AND user = '$userhash'");
$auth_array = array();
while ($result_object = $result_query->fetch(PDO::FETCH_NUM)) {
$result_array[] = $result_object;
}
while ($auth_object = $auth_query->fetch(PDO::FETCH_NUM)) {
$auth_array[] = $auth_object;
}
if(count($result_array) > 0){
if(isset($_COOKIE['PHPSESSID'])) {
//COOKIE is completely valid!
//Make a new cookie with the same session and another token.
$newusername = $auth_array[0][0];
$newsession = $session;
$newtoken = md5(uniqid(rand(), true));
$newuserhash = $newusername;
$value = "$newsession|$newtoken|$newuserhash";
$expire = time() + 4184000;
setcookie('remember', $value, $expire, '/', 'spigotpool.ml', isset($_SERVER["HTTPS"]), true);
$auth_query = $auth->prepare("UPDATE sessions
SET token = :newtoken, expire=:expire
WHERE session = :session
AND token = :token
AND user = :userhash");
$auth_query->bindParam(':newtoken', $newtoken);
$auth_query->bindParam(':expire', $expire);
$auth_query->bindParam(':session', $session);
$auth_query->bindParam(':token', $token);
$auth_query->bindParam(':userhash', $userhash);
$auth_query->execute();
//Set-up the whole session (with user details from database) etc...
}
} else if(count($auth_array) == 1) {
//TOKEN is different, session is valid
//This user is probably under attack
//Put up a warning, and let the user re-validate (login)
//Remove the whole session (also the other sessions from this user?)
} else {
//Cookie expired in database? Unlikely...
//Invalid in what way?
//Make a new cookie with the same session and another token.
$newusername = $_POST['user'];
$newsession = session_id();
$newtoken = md5(uniqid(rand(), true));
$newuserhash = md5($newusername);
$value = "$newsession|$newtoken|$newuserhash";
$expire = time() + 4184000;
setcookie('remember', $value, $expire, '/', 'www.example.com', isset($_SERVER["HTTPS"]), true);
$auth->query("INSERT INTO sessions (token, expire, session, user) VALUES ('$newtoken', '$expire', '$newsession', '$newuserhash')");
header('Location: index.php?action=logged-in');
}
} else {
//No cookie, rest of the script
//Make a new cookie with the same session and another token.
$newusername = $_POST['user'];
$newsession = session_id();
$newtoken = md5(uniqid(rand(), true));
$newuserhash = md5($newusername);
$value = "$newsession|$newtoken|$newuserhash";
$expire = time() + 4184000;
setcookie('remember', $value, $expire, '/', 'www.example.com', isset($_SERVER["HTTPS"]), true);
$auth->query("INSERT INTO sessions (token, expire, session, user) VALUES ('$newtoken', '$expire', '$newsession', '$newuserhash')");
header('Location: index.php?action=logged-in');
}
}
脚本的优点:
参考:http://jaspan.com/improved_persistent_login_cookie_best_practice