登录,用户身份验证,会话和csrf令牌

时间:2015-07-20 13:29:35

标签: php database session authentication

我在一个半月前开始学习PHP,我开始尝试创建自己的非常简单的CMS,以学习如何构建与mysql数据库交互的基本CRUD脚本等等。

我目前正在为CMS的私人管理部门构建我的登录表单,通过这样做,我想了解PHP中的基本安全性。

我想对用户身份验证进行一些基础研究,但我遇到的解释通常包括我最不了解的最佳安全实践信息。

所以,我查找了有关我不理解的小位的更多信息,其中包括我不熟悉的其他位,我被拖入吸收大量安全主题的信息,如通过默默无闻的安全性,单向密码哈希,安全通过适当的PHP配置设置,防范SQL注入,php注入,会话劫持等。

我无法实现这一切,因为我无法理解这一切。

所以第一个问题是关于将用户会话存储在数据库中。

此时我所知道的是,如果我使用的是共享主机,则可能无法正确配置服务器,并且其他人可能会访问服务器存储会话的文件夹。

这是我想将会话存储在数据库中的唯一原因吗?

其次,如何在db中存储会话来解决访问问题?

为了详细说明我的困惑,我很清楚,一旦会话存储在数据库中,它就可以安全地从共享主机上的人那里获得,但是如何告诉php - "嘿,我存储了我的用户数据库中的会话,将其从默认情况下指示存储它的任何地方排除"?

换句话说,仅仅因为我将会话存储在数据库中并不意味着服务器默认情况下将其从存储会话的地方排除。这是正确的,如果是我如何控制它?

第三,如何将会话实际存储在数据库中?我假设以下过程:

session_start();

//Assume a user has successfully logged in
//For better security regenerate the session on login
$session = session_regenerate_id();

$data[]= $session;

$query = $db_connection->prepare("INSERT INTO `sessions`(`session`) VALUES (?)");
$query->execute($data);

这是以最基本的形式将会话存储在数据库中的意思吗?

继续讨论下一个问题。

让我们假设我已经解决了上述问题。现在如何验证用户是否已登录?

通常我会做这样的事情:

if(!isset($_SESSION['user'])) {
    redirect_to('login.php');
}

但是由于用户会话存储在数据库中,我可以直接使用它还是我需要先将它从数据库中取出才能使用它?

我知道一旦会话开始,就会在浏览器名为PHPSESSID的加密/哈希(不确切知道它是哪一个)会话cookie。

但是由于我将会话存储在数据库中,这意味着我正在手动控制PHP通常自动执行的操作,在我的脑海中,流程链是生成 - > store-> encrypt / hash-&gt ;设置会话cookie。

这个假设是否正确?

最后跨站点伪造请求。

我的理解是,当用户登录时,他被欺骗以某种方式点击链接等,这将复制他的浏览器的cookie,现在攻击者拥有会话cookie并且可以冒充用户。

在这种情况下,如何将csrf令牌存储在表单上的隐藏字段中?

如果攻击者劫持了用户的会话,则会对会话检查令牌没有帮助,因为攻击者有一个有效的会话。

我错过了什么?

我的脑子里有关于事情如何运作的知识空白,我希望你能为我填补这些空白。

1 个答案:

答案 0 :(得分:1)

  1. 这是我想将会话存储在中的唯一原因 数据库?
  2. 数据库受密码保护,而会话目录则不受密码保护。不言而喻,大多数服务器都应该保护/ tmp / session路径(iirc),没有人可以访问它..但同样,你必须相当信任主机。

    1. 其次,如何在db中存储会话来解决访问问题?
    2. 查看答案#1

      1. 换句话说,仅仅因为我将会话存储在数据库中并不意味着服务器默认情况下将其从存储会话的地方排除。这是正确的,如果是我如何控制它?
      2. 基本上,会话使用唯一的标识符进行标识。向浏览器发送带有ID的cookie,服务器读取cookie ID并将其引用到服务器上的已配置位置。要设置保存路径,您可以使用session_set_save_handler - 这将允许您使用会话(保存时)执行任何操作 - 例如将其保存到数据库。参见示例#2。

        1. 现在如何验证用户是否已登录?
        2. **有多种方法可以确定用户是否已登录。基本方法是在会话中存储唯一数据并将其与users表关联。当浏览器发送会话ID时,服务器可以根据ID检索数据。然后,您可以获取此数据并将其与您的用户表交叉引用,以对用户进行身份验证。请记住,服务器(通常)是无状态的,意味着在页面之间导航,服务器不会在页面之间跟踪用户。因此使用会话。一个非常基本的例子,我不会使用它,是这样的:

          <?php
          function isLoggedIn() {
            if(!empty($_SESSION['user'])) {
              $uuid = $_SESSION['user']['uuid']; // universal unique id
              $username = $_SESSION['user']['username']; // username
              $last = $_SESSION['user']['last']; // last use of session
              if($last > time() - 600) { // active last 10 minutes?
                $stmt = $db->prepare("SELECT username FROM users WHERE uuid = :uuid");
                $stmt->bindValue(":uuid", $uuid, PDO::PARAM_STR);
                $user = $stmt->fetch(PDO::FETCH_ASSOC);
                if($username == $user['username']) { // user is logged in. uuid on session matches uuid in users table
                  $_SESSION['user']['last'] = time();
                  return true;
                }
              }
            }
          
            session_destroy(); // clear everything!
            return false;
          }
          ?>
          
          1. 但是由于用户会话存储在数据库中,我可以直接使用它还是我需要先将它从数据库中取出才能使用它?
          2. 不要将用户会话与用户对象本身混淆。会话用于将实际的USER和APP绑定在一起。见上面的答案&amp;示例

            1. 在这种情况下,如何将csrf令牌存储在我的表单上的隐藏字段中?
            2. 当您在表单中拥有CSRF令牌时,您还在服务器上拥有CSRF令牌...意味着当加载带有表单的页面时,会生成CSRF令牌并将其存储在特定于会话的会话中用户。请记住,拥有会话cookie的用户没有数据 - 只有标识符。表单还将包含隐藏字段,例如csrf_token。在POST时,服务器将比较post标记和会话。它还将重置/清除令牌,以便不再发布/使用它。这些令牌应该是唯一的......通常像md5(time())这样的东西效果很好。如果令牌不匹配或丢失,则可能存在攻击。此处采取的最佳操作是使用新的CSRF令牌再次显示表单。