登录、用户身份验证、会话和 csrf 令牌 [关闭]

Posted

技术标签:

【中文标题】登录、用户身份验证、会话和 csrf 令牌 [关闭]【英文标题】:Login, user authentication, session and csrf tokens [closed] 【发布时间】:2015-10-09 15:42:11 【问题描述】:

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

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

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

因此,我查找了有关我不了解的小部分的更多信息,其中包括我不熟悉的其他部分,并且我被拖入了吸收有关安全主题的大量信息,例如通过默默无闻的安全性,单向密码哈希,通过正确的 php 配置设置,防御 sql 注入、php 注入、会话劫持等安全性。

我无法实现所有这些,因为我无法真正理解这一切。

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

我现在知道的是,如果我使用共享主机,服务器可能配置不正确,服务器存储会话的文件夹可能会被其他人访问。

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

二、将session存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');

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

我知道,一旦会话开始,浏览器中就会有一个加密/散列(不知道到底是哪一个)会话 cookie,名为 PHPSESSID。

但由于我将会话存储在数据库中,这意味着我正在手动控制 PHP 通常自动执行的操作,在我的脑海中,流程链是生成->存储->加密/哈希->设置会话饼干。

这个假设正确吗?

最后是跨站点伪造请求。

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

存储在表单隐藏字段中的 csrf 令牌在这种情况下有何帮助?

如果攻击者劫持了用户的会话,则检查该会话的令牌将无济于事,因为攻击者拥有一个有效的会话。

我错过了什么?

在我的脑海中存在关于事情如何运作的知识空白,我希望你能帮我填补这些空白。

【问题讨论】:

您在一个问题中提出了很多问题,您应该将它们隔离开来,因为现在它太大而无法正确回答。 【参考方案1】:
    这是我想将会话存储在 数据库?

数据库受密码保护,会话目录不受密码保护。不用说,大多数服务器应该保护 /tmp/session 路径 (iirc) 并且没有人可以访问它。但同样,您必须非常信任主机。

    二、将session存db如何解决访问问题?

查看答案#1

    换句话说,仅仅因为我将会话存储在数据库中并不意味着服务器将其排除在默认存储会话的位置之外。这是正确的吗?如果是,我该如何控制?

基本上,会话使用唯一的标识符来标识。浏览器收到带有 ID 的 cookie,服务器读取 cookie ID 并将其引用到服务器上配置的位置。要设置保存路径,您可以使用session_set_save_handler - 这将允许您对会话执行任何操作(保存时) - 例如将其保存到数据库。请参见示例 #2。

    现在,如果用户已登录,我该如何进行身份验证?

**有多种方法可以确定用户是否已登录。一种基本方法是将唯一数据存储在会话中并将其与用户表相关联。当浏览器发送会话 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;

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

不要将用户会话与用户对象本身混淆。会话用于将实际的 USER 和 APP 绑定在一起。请参阅上面的答案和示例

    存储在表单隐藏字段中的 csrf 令牌在这种情况下有何帮助?

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

【讨论】:

以上是关于登录、用户身份验证、会话和 csrf 令牌 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

Laravel - 使用会话令牌进行身份验证

SPA 使用的经过身份验证的 Rest API:如何获取用于登录和注册表单的 CSRF 令牌?

CSRF 保护

Laravel CSRF 保护

OpenID-Connect 身份验证服务器的登录页面中是不是需要 CSRF 令牌?

使用会话令牌或随机数进行跨站点请求伪造保护 (CSRF)?