Правильный способ использования функциональности "Запомнить меня" в PHP

Short

работа над системой входа в систему и попытка реализовать функцию remember me.

недавно я провел исследование по этой теме, прочитал кучу статей, сообщений, рассказов, романов, сказок (называя их так, потому что некоторые из них не содержат даже 1 строку кода, просто множество слов) об уязвимостях cookie, таких как фиксация, угон ... так далее.

и решил достичь следующих целей

  1. установить задержку по времени между попытками входа (чтобы предотвратить атаки bruteforce) и ограничить количество попыток
  2. для регенерации идентификатора сеанса почти на каждой операции

но Я действительно запутался в своей основной проблеме: какой путь правильный, для функции "Запомнить меня"? использовать cookies / session / database?

и, пожалуйста, объясните свою идею о коде.(Я не могу ясно понять без кода)

полная

в настоящее время мой код выглядит как это

во время входа в систему я использую следующую функцию для установки cookies и сеанса

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;
}

затем на защищенных страницах пользователя, проверка на user_id использование user_id для извлечения всех важных данных о пользователе из 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();
                }
            }
        }

4 ответов


но я действительно запутался в своей основной проблеме: какой путь правильный, для функции "Запомнить меня"? использовать cookies / session / database?

http является protocall без гражданства. Маркер аутентификации должен сохраняться, чтобы сохранить состояние. Правильный способ - использовать сеанс. Как вы отслеживаете сеанс? Это до вас. Но печенье не плохое.

в сеансе вы можете сохранить хэш, созданный из разных критериев браузера (агент пользователя, ОС, разрешение экрана и т. д.), Чтобы проверьте, является ли маркер из той же среды. Чем больше критериев вы сохраните, тем сложнее будет угнать. Кстати, вам нужен JavaScript, чтобы каждый раз захватывать дополнительную информацию.


установить временную задержку между попытками входа в систему (для предотвращения атак bruteforce) и ограничить количество попыток

Итак, вы предоставляете метод для DOS по учетной записи?

для регенерации идентификатора сеанса почти на каждой операции

ЭМ, нет. Это, скорее всего, уничтожит объект. Вы всегда должны генерировать новый id, когда текущий id истек или когда пользователь аутентифицируется-в противном случае оставьте его один.

но я действительно запутался в своей основной проблеме: какой путь правильный, для функции "Запомнить меня"? использовать cookies / session / database?

поскольку вам нужно сохранить токен на клиенте, это означает cookies (если вы не хотите написать что-то действительно сложное с использованием локального хранилища). Поскольку вы не хотите выставлять данные через cookie / сделать подделку простой, это означает, что это должно быть случайное значение. И для согласования сохраненного случайного значения, это означает хранение данных на стороне сервера-вероятно, в базе данных, поскольку должна быть возможность ссылаться на данные на основе идентификатора пользователя или на основе случайного значения.

в то время как вы можете просто использовать не истекающий (или очень долгоживущий) сеанс, я бы держался подальше от этого-данные будут снежным комком - и это хорошая идея, чтобы обновить данные сеанса время от времени.

Вам также нужно угодить для сценария где потребитель хочет вас вспомнить ее на 2 различных компьютерах. Если вы только держите один токен "Запомнить меня" для каждой учетной записи, тогда вам придется использовать одно и то же значение на обоих клиентах или удалить старый токен при создании нового (т. е. пользователя можно запомнить только на одной машине).

пожалуйста, объясните свою идею о коде. Я не могу ясно понять без кода

нет. Мне платят за написание кода; Если вы хотите, чтобы я написал код для вас, вам придется заплатить мне. И код займет гораздо больше места и времени, чем описание выше.


Если вы хотите пример функции "Запомнить меня" с кодом, вы можете проверить мою библиотеку PHP вhttps://github.com/gbirke/rememberme


Cookies-лучшее решение. Проверьте следующий код:

<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";
    }
}