使用“记住我”的正确方法PHP 中的功能

发布于 2024-12-27 09:29:03 字数 3322 浏览 3 评论 0原文

简短

介绍登录系统并尝试实现“记住我”功能。

最近,我做了关于这个主题的研究,阅读了一堆关于cookie漏洞的文章、帖子、故事、小说、童话故事(这样称呼它们,因为其中一些甚至不包含一行代码,只是大量的文字)例如固定、劫持...等。

并决定实现以下目标

  1. 设置登录尝试之间的时间延迟(以防止暴力攻击)并限制尝试计数
  2. 几乎每次操作都重新生成会话 ID

但是我真的很困惑我的主要问题:对于“记住我”功能,哪种方式是正确的?使用cookie/会话/数据库?

请解释您对代码的想法。(没有代码我无法清楚地理解)

详细

目前,我的代码看起来像这样

在登录期间我使用以下函数来设置cookie和会话

protected function validateUser($userid, $ckey=0, $rememmber=0) {
    session_start();
    session_regenerate_id(true); //this is a security measure
    $_SESSION['user_id'] = $userid;
    $_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
    if (isset($remember) && $rememmber == 'on') {
        setcookie("user_id", $_SESSION['user_id'], time() + 60 * 60 * 24 * COOKIE_TIME_OUT, "/");
        setcookie("user_key", sha1($ckey), time() + 60 * 60 * 24 * COOKIE_TIME_OUT, "/");
    }
    return true;
}

然后在安全用户页面上检查< code>user_id 使用 user_id 从数据库获取有关用户的所有重要数据

public function protect() {
        session_start();

        /* Secure against Session Hijacking by checking user agent */
        if (isset($_SESSION['HTTP_USER_AGENT'])) {
            if ($_SESSION['HTTP_USER_AGENT'] != md5($_SERVER['HTTP_USER_AGENT'])) {
                $this->signout();
                exit;
            }
        }

// before we allow sessions, we need to check authentication key - ckey and ctime stored in database

        /* If session not set, check for cookies set by Remember me */
        if (!isset($_SESSION['user_id'])) {
            if (isset($_COOKIE['user_id']) && isset($_COOKIE['user_key'])) {
                /* we double check cookie expiry time against stored in database */

                $cookie_user_id = $_COOKIE['user_id'];
                               $stmt = $this->db->prepare("select `ckey`,`ctime` from `users` where `id` =?") or die($this->db->error);
            $stmt->bind_param("i", $cookie_user_id) or die(htmlspecialchars($stmt->error));
            $stmt->execute() or die(htmlspecialchars($stmt->error));
            $stmt->bind_result($ckey, $ctime) or die($stmt->error);
            $stmt->close() or die(htmlspecialchars($stmt->error));
                // coookie expiry
                if ((time() - $ctime) > 60 * 60 * 24 * COOKIE_TIME_OUT) {
                    $this->signout();
                }
                /* Security check with untrusted cookies - dont trust value stored in cookie.       
                  /* We also do authentication check of the `ckey` stored in cookie matches that stored in database during login */

                if (!empty($ckey) && is_numeric($_COOKIE['user_id']) && $_COOKIE['key'] == sha1($ckey)) {
                    session_regenerate_id(); //against session fixation attacks.

                    $_SESSION['user_id'] = $_COOKIE['user_id'];
                    $_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
                } else {
                    $this->signout();
                }
            } else {
                if ($page != 'main') {
                    header('Location:' . wsurl);
                    exit();
                }
            }
        }

Short

Working on login system and trying to implement remember me feature.

Recently, l did research about this subject, read bunch of articles, posts, stories, novels, fairy tales (calling them so, because some of them doesn't contain even 1 line of code, just loads of words) about, cookie vulnerabilities such as fixation, hijacking ... etc.

And decided to achieve following targets

  1. To set time delay between login attempts (to prevent bruteforce attacks) and to limit attempts count
  2. To regenerate session id on nearly every operation

But I really confused about my main problem: which way is proper, for "remember me" feature? to use cookies/session/database?

And please explain your idea on code.(I can't understand clearly without code)

Detailed

Currently, my code looks like that

During sign-in I'm using following function to set cookies and session

protected function validateUser($userid, $ckey=0, $rememmber=0) {
    session_start();
    session_regenerate_id(true); //this is a security measure
    $_SESSION['user_id'] = $userid;
    $_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
    if (isset($remember) && $rememmber == 'on') {
        setcookie("user_id", $_SESSION['user_id'], time() + 60 * 60 * 24 * COOKIE_TIME_OUT, "/");
        setcookie("user_key", sha1($ckey), time() + 60 * 60 * 24 * COOKIE_TIME_OUT, "/");
    }
    return true;
}

Then on secure user pages, checking for user_id using user_id to fetch all important data about user from db

public function protect() {
        session_start();

        /* Secure against Session Hijacking by checking user agent */
        if (isset($_SESSION['HTTP_USER_AGENT'])) {
            if ($_SESSION['HTTP_USER_AGENT'] != md5($_SERVER['HTTP_USER_AGENT'])) {
                $this->signout();
                exit;
            }
        }

// before we allow sessions, we need to check authentication key - ckey and ctime stored in database

        /* If session not set, check for cookies set by Remember me */
        if (!isset($_SESSION['user_id'])) {
            if (isset($_COOKIE['user_id']) && isset($_COOKIE['user_key'])) {
                /* we double check cookie expiry time against stored in database */

                $cookie_user_id = $_COOKIE['user_id'];
                               $stmt = $this->db->prepare("select `ckey`,`ctime` from `users` where `id` =?") or die($this->db->error);
            $stmt->bind_param("i", $cookie_user_id) or die(htmlspecialchars($stmt->error));
            $stmt->execute() or die(htmlspecialchars($stmt->error));
            $stmt->bind_result($ckey, $ctime) or die($stmt->error);
            $stmt->close() or die(htmlspecialchars($stmt->error));
                // coookie expiry
                if ((time() - $ctime) > 60 * 60 * 24 * COOKIE_TIME_OUT) {
                    $this->signout();
                }
                /* Security check with untrusted cookies - dont trust value stored in cookie.       
                  /* We also do authentication check of the `ckey` stored in cookie matches that stored in database during login */

                if (!empty($ckey) && is_numeric($_COOKIE['user_id']) && $_COOKIE['key'] == sha1($ckey)) {
                    session_regenerate_id(); //against session fixation attacks.

                    $_SESSION['user_id'] = $_COOKIE['user_id'];
                    $_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
                } else {
                    $this->signout();
                }
            } else {
                if ($page != 'main') {
                    header('Location:' . wsurl);
                    exit();
                }
            }
        }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

熟人话多 2025-01-03 09:29:03

设置登录尝试之间的时间延迟(以防止暴力攻击)并限制尝试次数

那么您正在通过帐户提供 DOS 方法吗?

几乎每次操作都重新生成会话 ID

,不行。这实际上很可能会击败该物体。当当前 ID 过期或用户通过身份验证时,您应该始终生成一个新 ID - 否则不要管它。

但我真的对我的主要问题感到困惑:对于“记住我”功能,哪种方式是正确的?使用cookie/会话/数据库?

由于您需要在客户端保留令牌,这意味着 cookie(除非您喜欢使用本地存储编写非常复杂的东西)。由于您不想通过 cookie 暴露数据/使伪造变得简单,这意味着它应该是一个随机值。为了协调存储的随机值,这意味着在服务器端存储数据 - 可能在数据库中,因为必须可以基于用户 ID 或基于随机值引用数据。

虽然您可以只使用不会过期(或寿命很长)的会话,但我会远离这种情况 - 数据会滚雪球 - 并且偶尔更新会话数据是个好主意。

您还需要满足用户希望您在两台不同计算机上记住她的情况。如果您为每个帐户只持有一个“记住我”令牌,那么您必须在两个客户端上使用相同的值,或者在创建新令牌时删除旧令牌(即只能在一台计算机上记住用户) )。

请解释一下你对代码的想法。没有代码我无法清楚理解

。我写代码是拿钱的;如果您想让我为您编写代码,那么您需要付钱给我。而且代码会比上面的描述占用更多的空间和时间。

To set time delay between login attempts (to prevent bruteforce attacks) and to limit attempts count

So you're providing a method for DOS by account?

To regenerate session id on nearly every operation

erm, no. That's actually likely to defeat the object. You should always generate a new id when the current id is expired or when the user is authenticated - otherwise leave it alone.

But I really confused about my main problem: which way is proper, for "remember me" feature? to use cookies/session/database?

Since you need to retain a token on the client, that means cookies (unless you fancy writing something really complicated using local storage). Since you don't want to expose data via the cookie / make forgery simple that means it should be a random value. And in order to reconcile the stored random value, that means storing data serverside - probably in a database since it must be possible to reference the data based on the user id or based on the random value.

While you could just use a non-expiring (or very long lived) session, I'd stay away from this - the data will snowball - and it's a good idea to renew the session data once in a while.

You also need to cater for the scenario where a user wants you to remember her on 2 different computers. If you only hold a single 'remember me' token for each account, then either you'll have to use the same value at both clients or delete the old token when you create a new one (i.e. user can only be remembered on one machine).

please explain your idea on code. I can't understand clearly without code

No. I get paid to write code; If you want me to write the code for you then you'll need to pay me. And the code will take up much more space and time than the description above.

饮湿 2025-01-03 09:29:03

如果您想要带有代码的“记住我”函数的示例,您可以在 https:// 查看我的 PHP 库github.com/gbirke/rememberme

If you want an example of a "remember me" function with code, you can check out my PHP library at https://github.com/gbirke/rememberme

孤单情人 2025-01-03 09:29:03

但我真的对我的主要问题感到困惑:对于“记住我”功能,哪种方式是正确的?使用 cookies/会话/数据库?

Http 是一种无状态的协议调用。身份验证令牌必须持续存在才能保持状态。
正确的方法是使用session。现在如何跟踪会话?由你决定。但饼干也不错。

在会话中,您可以保存根据浏览器不同条件(用户代理、操作系统、屏幕分辨率等)创建的哈希值,以检查令牌是否来自同一环境。您保存的标准越多,劫持就越困难。顺便说一句,你每次都需要 JavaScript 来获取额外的信息。

But I really confused about my main problem: which way is proper, for "remember me" feature? to use cookies/session/database?

Http is a stateless protocall. Authentication token must persist to keep the state.
Proper way is to use session. Now how do you track the session? It's up to you. But cookies are not bad.

In the session you can save a hash created from browser different criteria(user agent, os, screen resolution etc) to check if the token is from same environment. The more criteria you save the more itll be harder to hijack. Btw you need JavaScript to grab ths extra information every time.

薄荷梦 2025-01-03 09:29:03

Cookie 是最好的解决方案。查看以下代码:

<form action="" method="post" id="frmLogin">
<div class="error-message"><?php if(isset($message)) { echo $message; } ?></div>    
<div class="field-group">
    <div><label for="login">Username</label></div>
    <div><input name="member_name" type="text" value="<?php if(isset($_COOKIE["member_login"])) { echo $_COOKIE["member_login"]; } ?>" class="input-field">
</div>
<div class="field-group">
    <div><label for="password">Password</label></div>
    <div><input name="member_password" type="password" value="<?php if(isset($_COOKIE["member_password"])) { echo $_COOKIE["member_password"]; } ?>" class="input-field"> 
</div>
<div class="field-group">
    <div><input type="checkbox" name="remember" id="remember" <?php if(isset($_COOKIE["member_login"])) { ?> checked <?php } ?> />
    <label for="remember-me">Remember me</label>
</div>
<div class="field-group">
    <div><input type="submit" name="login" value="Login" class="form-submit-button"></span></div>
</div>       

session_start();
if(!empty($_POST["login"])) {
    $conn = mysqli_connect("localhost", "root", "", "blog_samples");
    $sql = "Select * from members where member_name = '" . $_POST["member_name"] . "' and member_password = '" . md5($_POST["member_password"]) . "'";
    $result = mysqli_query($conn,$sql);
    $user = mysqli_fetch_array($result);
    if($user) {
            $_SESSION["member_id"]         = $user["member_id"];

            if(!empty($_POST["remember"])) {
                setcookie ("member_login",$_POST["member_name"],time()+ (10 * 365 * 24 * 60 * 60));
                setcookie ("member_password",$_POST["member_password"],time()+ (10 * 365 * 24 * 60 * 60));
            } else {
                if(isset($_COOKIE["member_login"])) {
                    setcookie ("member_login","");
                }
                if(isset($_COOKIE["member_password"])) {
                    setcookie ("member_password","");
                }
            }
    } else {
        $message = "Invalid Login";
    }
}

Cookies are best solution. Check out the following code:

<form action="" method="post" id="frmLogin">
<div class="error-message"><?php if(isset($message)) { echo $message; } ?></div>    
<div class="field-group">
    <div><label for="login">Username</label></div>
    <div><input name="member_name" type="text" value="<?php if(isset($_COOKIE["member_login"])) { echo $_COOKIE["member_login"]; } ?>" class="input-field">
</div>
<div class="field-group">
    <div><label for="password">Password</label></div>
    <div><input name="member_password" type="password" value="<?php if(isset($_COOKIE["member_password"])) { echo $_COOKIE["member_password"]; } ?>" class="input-field"> 
</div>
<div class="field-group">
    <div><input type="checkbox" name="remember" id="remember" <?php if(isset($_COOKIE["member_login"])) { ?> checked <?php } ?> />
    <label for="remember-me">Remember me</label>
</div>
<div class="field-group">
    <div><input type="submit" name="login" value="Login" class="form-submit-button"></span></div>
</div>       

session_start();
if(!empty($_POST["login"])) {
    $conn = mysqli_connect("localhost", "root", "", "blog_samples");
    $sql = "Select * from members where member_name = '" . $_POST["member_name"] . "' and member_password = '" . md5($_POST["member_password"]) . "'";
    $result = mysqli_query($conn,$sql);
    $user = mysqli_fetch_array($result);
    if($user) {
            $_SESSION["member_id"]         = $user["member_id"];

            if(!empty($_POST["remember"])) {
                setcookie ("member_login",$_POST["member_name"],time()+ (10 * 365 * 24 * 60 * 60));
                setcookie ("member_password",$_POST["member_password"],time()+ (10 * 365 * 24 * 60 * 60));
            } else {
                if(isset($_COOKIE["member_login"])) {
                    setcookie ("member_login","");
                }
                if(isset($_COOKIE["member_password"])) {
                    setcookie ("member_password","");
                }
            }
    } else {
        $message = "Invalid Login";
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文