我的php“记住我”系统安全/实用吗?

时间:2012-06-18 21:49:52

标签: php security cookies

我目前正在为网络项目实施“记住我”系统。在阅读了几篇文章之后(虽然不是那么有用的文章)“我想出了这个系统,以防止cookie被用来访问除被分配给的用户以外的帐户:

  1. 用户登录并检查“记住我”框。
  2. 用户电子邮件和密码组合已通过身份验证。因为检查了记住我的功能,所以创建了一个长度为128个字符的随机盐,并将其与用户userid一起存储在表中。设置一个cookie,其中包含用户userid的哈希值,该用户ID与用户userid和salt的哈希串联。
  3. 当用户重新访问网页时,将分离用户标识和散列,并根据从存储在数据库中的salt和userid生成的散列检查散列。如果值匹配,则用户是分配了cookie并且用户已登录的用户。
  4. 如果可以提出任何问题/改进,请指出它我总是受到批评:)

5 个答案:

答案 0 :(得分:3)

你的系统有一个良好的开端,但仍然非常容易被劫持(如果有人接受存储在cookie中的哈希,他可以轻松接管会话。)

无论如何,这里有一些关于同一主题的好帖子:

我认为你应该从阅读这些链接开始。

确实没有一个完整的万无一失的系统(检查第3和第4个链接,它们提供了一些有趣的额外的东西;用户代理也是你可以使用的某种标识符)。我建议你使用某种OpenID提供程序(或多个)来获取所有安全性的东西。如果你想自己做这件事:你必须权衡专业人士和骗子以达到最大安全性或少一点。

答案 1 :(得分:2)

像其他人一样说你有一个好的开始。我建议创建一个存储一些cookie信息的表,特别是:

user_id
random_hash
ip_address
date_created

然后当用户登录时,您生成一个像您所说的哈希并将该数据插入表中并创建存储其user_id&的cookie。哈希。

当用户返回时,检查hash和ip_address并查找匹配项。如果找到一个,请验证所述user_id AND 删除已使用的哈希值,并为下次登录生成一个新哈希值。我相信你可以看到这样做的好处。

此外 - 确保您使用SSL和(重要)绝不允许用户在不提供密码的情况下更改或查看任何关键信息!

干杯!

答案 2 :(得分:1)

不,这太复杂了。

  1. 使用列remember_me

  2. 创建数据库中的user_id | hash(或您喜欢的任何表名称)表
  3. 在用“记住我”选项对用户进行身份验证后,生成随机哈希,并将其放入与对应的user_id和用户的cookie中的表中

  4. 当用户访问某个页面并且未经过身份验证且具有“remember_me”Cookie时 - 按Cookie值查找user_id

  5. 重要的是:没有正当理由依赖您的remember_me客户数据真实用户的姓名,电子邮件,密码或其他任何东西

    PS:如果您愿意,可以使用user_id值附加客户端数据

答案 3 :(得分:0)

是的,听起来不错,但为什么重新发明轮子?您正在描述会话。您也可以使用PHP内置的功能。

http://php.net/manual/en/features.sessions.php

答案 4 :(得分:0)

我想我找到了一个聪明的解决方案!

此(复杂?)脚本的优点:

  • 当用户在选中“记住我”的情况下成功登录时,除了标准会话管理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)字符的字符串。
  • Cookie中的
  • Userhash将是一个由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