我想我找到了一个聪明的解决方案!
这个(复杂的?)脚本的优点:
当用户在选中记住我的情况下成功登录时,除了标准会话管理 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
在 cookie 中将是 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');
}
}
脚本的优点:
- 多次登录。您可以为您所在的每台计算机创建新会话。
- Cookie 和数据库将保持清洁。活跃用户每次登录都会更新 cookie。
- 一开始的会话检查确保数据库不会收到无用的请求。
- 如果攻击者窃取了 cookie,它会获得一个新的令牌,但不会获得一个新的会话。因此,当真正的用户使用旧的(无效的)令牌但使用有效的用户会话组合访问网站时,用户会收到潜在盗窃的警告。通过登录重新验证后,会创建一个新会话,并且攻击者持有的会话无效。重新验证确保受害者确实是受害者,而不是攻击者。
参考:http: //jaspan.com/improved_persistent_login_cookie_best_practice