Правильный способ использования функции «Запомнить меня» в PHP - PullRequest
15 голосов
/ 18 января 2012

Short

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

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

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

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

Но Я действительно запутался в своей главной проблеме: какой путь правильный, для "помните меня "особенность?использовать куки / сеанс / базу данных?

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

Подробно

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

Во время входа в систему я использую следующую функцию для установки файлов 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;
}

Затем на защищенных пользовательских страницах проверяю 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 ]

8 голосов
/ 18 января 2012

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

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

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

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

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

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

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

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

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

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

3 голосов
/ 18 января 2012

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

2 голосов
/ 18 января 2012

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

Http - протокол без сохранения состояния.Токен аутентификации должен сохраняться для сохранения состояния.Правильный способ - использовать сессию.Теперь, как вы отслеживаете сеанс?Тебе решать.Но куки - это неплохо.

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

0 голосов
/ 05 июня 2018

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

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