首页 > 解决方案 > 会话/cookie 用户定期注销

问题描述

我的用户已经从我的网站登出。当有人从另一台设备登录时会发生这种情况。我调查了一下,注意到来自远程 IP 的人已经能够以不同的用户身份登录我的网站。

我不知道我是否故意成为目标,或者是否是我的代码中的某种错误。我想知道如何以安全的方式正确设置会话 cookie 以防止这种情况发生。

  1. 我的 cookie 只是 HTTP

  2. 我也不认为这是中间人攻击,因为来自不同区域的多个用户正在注销。我的网站是 SSL 安全的。

  3. 我不相信这是暴力攻击。有时当我登录时,在 30 秒内我再次注销并且远程 IP 已登录。

  4. 我不相信他可以访问任何密码。一切都在数据库中进行哈希处理,唯一存储在客户端上的是会话 HTTP ONLY cookie 令牌。

我很困。

这是我的登录脚本,它检查用户的凭据并设置会话:

//database connection is $db_connect

//Creates a random String
function generateRandomString() {
    $length = rand(25, 30);
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}

//Grab email, password, IP address
$email_attempt=strtoupper(preg_replace('#[^a-z0-9\.\@\_\-\+]#i', '', $_POST['e']));
$password_attempt=$_POST["p"];
$user_IP = preg_replace('#[^0-9.\:]#', '', getenv('REMOTE_ADDR'));


//Call the database
$sql = "SELECT password, userID FROM user_data WHERE email='$email_attempt' LIMIT 1";
$query = mysqli_query($db_connect, $sql);
if(mysqli_num_rows($query)>0)
{

    //Get the ID and hashed password from database
    while($row = $query->fetch_assoc()) {
        $password_db=$row["password"];
        $userID_db=$row["userID"];
    }


    //Verify Login using password verify
    if(password_verify($password_attempt, $password_db)==true){

        //remove any previous sessions for the user
        $sql = "DELETE FROM user_sessions WHERE userID='$userID_db'";
        $query = mysqli_query($db_connect, $sql);


        //Clear any current cookies from client
        if(isset($_COOKIE["user"]) && isset($_COOKIE["token"])) {
            setcookie("user", '', strtotime( '-5 days' ), '/');
            setcookie("token", '', strtotime( '-5 days' ), '/');
        }
        session_destroy();


        // Set Session data to an empty array
        $_SESSION = array();


        //create a new token, set new session and cookies 
        $token=generateRandomString();
        $_SESSION['user'] = $userID_db;
        $_SESSION['token'] = $token;
        setcookie("user", $userID_db, strtotime( '+3 days' ), "/", "", "", TRUE);
        setcookie("token", $token, strtotime( '+3 days' ), "/", "", "", TRUE);


        //Hash the token to store in the database
        $token_hash=password_hash($token, PASSWORD_DEFAULT);

        //Store session into database
        $sql = "INSERT INTO user_sessions 
            (userID, session_token, IP, loginDate) 
            VALUES 
            ('$userID_db','$token_hash','$user_IP', '$current_date')";

        $query = mysqli_query($db_connect, $sql);

        header("Location: dashboard.php");
    }

    else
    {
        echo 'wrong_credentials';
    }
}

这是我通过检查会话和 cookie来评估用户并查看他们是否已登录的代码;

//start session
session_start();

$user_ok = false;
$clientID = "";
$token = "";
$user_IP = preg_replace('#[^0-9.\:]#', '', getenv('REMOTE_ADDR'));

if(isset($_SESSION["user"]) && isset($_SESSION["token"])) {
    //Get session
    $clientID = preg_replace('#[^a-z0-9]#i', '', $_SESSION['user']);
    $token = preg_replace('#[^a-z0-9]#i', '', $_SESSION['token']);


    // Verify the user with session data
    $user_ok = checkUser($db_connect,$clientID,$token,$user_IP);



} else if(isset($_COOKIE["user"]) && isset($_COOKIE["token"])){

    //Set session from cookie
    $_SESSION['user'] = preg_replace('#[^a-z0-9]#i', '', $_COOKIE['user']);
    $_SESSION['token'] = preg_replace('#[^a-z0-9]#i', '', $_COOKIE['token']);


    //Get session data
    $clientID =  preg_replace('#[^a-z0-9]#i', '', $_SESSION['user']);;
    $token =  preg_replace('#[^a-z0-9]#i', '', $_SESSION['token']);

    // Verify the user
    $user_ok = checkUser($db_connect,$clientID,$token,$user_IP);
} 




// User Verify function
function checkUser($db_connect,$user,$token,$ip){

    //Grab the session
    $sql = "SELECT session_token FROM user_sessions WHERE userID='$user' AND ip='$ip' LIMIT 1";
    $query = mysqli_query($db_connect, $sql);
    if(mysqli_num_rows($query)>0){

        $row=mysqli_fetch_row($query);

        $token_hashed=$row[0];

        //compare token given and hashed token
        if(password_verify($token,$token_hashed)==true)
        {
            return true;
        }

        else
        {
            return false;
        }
    } 

    else
    {
        return false;
    }
}

所以我的问题是:这段代码足够安全吗?

我怎样才能更安全?

另外,如果您对这个谜题中发生的事情有任何想法,请告诉我,因为我正在被摧毁。先感谢您。

标签: phpmysqlsessionsession-cookies

解决方案


经过一番挖掘,我意识到问题在于试图将会话锚定到单个 IP 地址。

我们在我们的站点上运行 CloudFlare,因此getenv('REMOTE_ADDR')有时会返回 CloudFlare 的 IP 地址而不是客户端 IP 地址。这导致了注销。

但我想我可以根据评论回答我自己的问题。为了使脚本更安全并停止定期注销,我应该:

  1. 始终使用准备好的语句来防止 SQL 注入
  2. 不要将会话绑定到 IP 地址,因为它们可能会发生变化(尤其是在移动设备上)

奥卡姆剃刀是解决问题的原则,它本质上表明更简单的解决方案比复杂的解决方案更可能是正确的。


推荐阅读