【问题标题】:PHP User Authentication through Email通过电子邮件进行 PHP 用户身份验证
【发布时间】:2014-01-25 06:45:59
【问题描述】:

我正在开发一个 php/laravel-4 项目,我们需要对来自我们发送给他们的电子邮件中的链接的用户进行自动身份验证,我们需要对链接设置时间限制,这样电子邮件中的链接就不会进行身份验证过期时间过去后,我采用了这种方法,但我对它的安全性有些怀疑:

首先,我使用用户的电子邮件、时间戳和密钥创建一个 md5 哈希,如下所示:

$timestamp = time();
$hash = md5($email . $timestamp . $secret_key);

然后我可以生成这样的网址:

$url = "http://www.example.com/url?email={$email}&hash={$hash}&timestamp={$timestamp}

然后我可以检查时间戳(用于时间验证)并重新生成哈希并使用提供的电子邮件对用户进行身份验证,您认为它有任何安全漏洞吗?如果是,请向我推荐安全的方法。

【问题讨论】:

  • 如何防止有人在目标用户之前嗅探网络流量并使用此链接?可以安全地假设您的许多用户将通过未加密的方法检查电子邮件。
  • 为什么要包含时间戳?哈希本身在理论上是唯一的,因此发送 ONLY 哈希,并将关联的用户 ID/时间戳存储在数据库表中。当用户单击链接时,您会在表中查找哈希,检查它在时间上是否仍然有效,然后获取该点的用户 ID。
  • @JohnChrysostom 那么您对通过电子邮件验证用户有什么建议?
  • @MarcB 发送时间戳和电子邮件的风险是什么?因为我想消除数据库开销。
  • 我建议至少在他们点击链接后仍然需要他们的密码。要么通过电子邮件进行身份验证......

标签: php security authentication hash laravel


【解决方案1】:

只要密钥不是用户密码,我觉得这很好。

【讨论】:

    【解决方案2】:

    您不会找到完全安全的方法来执行此操作。如果某人的电子邮件被黑了怎么办?或其他一百万种情况。

    我认为您最好的选择是发送一封电子邮件,其中包含经过哈希处理的用户名(或电子邮件),并可能再次包含时间戳。一旦他们单击链接,他们必须输入密码,然后继续前进。

    您还可以在电子邮件链接中包含重定向链接,以便脚本可以在他们输入密码后将其重定向到适当的位置。

    【讨论】:

      【解决方案3】:

      md5 不是密码安全的,因为它容易破解(在密码方面)。

      主要关注的是md5 ,你不希望这样,你希望它尽可能慢,但不要太慢以至于生成时间过长并验证。

      PHP password guide,这里的关键是cost参数。更高的成本意味着它更安全。 cost 所做的本质上是:它增加了哈希函数的应用次数,更多的应用意味着它需要更长的时间来产生,因此是蛮力的。


      您可以做的另一件事是混淆您的输入。如果您可以将网址更改为:

      $string = $timestamp . "|" . $email . "|" . $hash;
      // then encrypt this string
      $encrypted = mcrypt_encrypt($cypher, $secret_password_2, $string); // or any other two-way encryption
      $url = $base . "?hash=" . $encrypted;
      

      当你收到哈希解密它,拆分它并检查哈希。这使得正在发生的事情变得不那么明显。所有 3 个值都打包在一个加密的 blob 中。

      【讨论】:

      • 这最终会变成一个很长的 $encrypted 字符串。
      • URL 的实际限制是 2000 个字符,我们甚至没有接近!可能有 100 个?
      【解决方案4】:

      我不会那样做。我会做什么:

      为您的链接创建一个表格:

      public function up()
      {
          Schema::create('login', function($table) {
              $table->string('id')->primary();
      
              $table->string('user_id');
      
              $table->timestamps();
          });
      }
      

      每次生成链接时,都会在此表中添加一行:

      $user = User::find(1);
      
      $login = Login::create(['id' => Login::generateID(), 'user_id' => $user->id]);
      
      $url = "http://www.example.com/url?login_id={$login->id}"
      

      然后,当您的用户单击该链接时,您可以自动让他登录,同时立即使该链接失效:

      $login = Login::findOrFail(Input::get('login_id'));
      
      $user = User::find($login->user_id);
      
      Auth::login($user);
      
      $login->delete();
      

      并创建一个 Artisan 命令以定期删除该表上的旧记录:

      Login::where('created_at', '<=', Carbon\Carbon::now()->subDays(2))->delete();
      

      这可以是generateID() 的代码,这是一个基本的 UUID 代码生成:

      public static function v4() 
      {
          return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
      
          // 32 bits for "time_low"
          mt_rand(0, 0xffff), mt_rand(0, 0xffff),
      
          // 16 bits for "time_mid"
          mt_rand(0, 0xffff),
      
          // 16 bits for "time_hi_and_version",
          // four most significant bits holds version number 4
          mt_rand(0, 0x0fff) | 0x4000,
      
          // 16 bits, 8 bits for "clk_seq_hi_res",
          // 8 bits for "clk_seq_low",
          // two most significant bits holds zero and one for variant DCE1.1
          mt_rand(0, 0x3fff) | 0x8000,
      
          // 48 bits for "node"
          mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
          );
      }
      

      您的系统上没有任何附加条件。

      【讨论】:

      • 为什么不直接使用随机 md5 哈希?
      • 您在查找 id 时仍应检查时间戳。如果由于某种原因在清理过程中存在漏洞,您可能会得到陈旧的结果。您也可以在检查时运行清理,或者在检查时间的 1/10 时运行。
      • 我并不是想给出一个完整的代码实现,只是一个更好的方法的指南,如何真正实现它取决于 OP。刚刚更改了 v4 的实​​现,您是对的。
      猜你喜欢
      • 1970-01-01
      • 2018-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-21
      • 2018-04-01
      • 1970-01-01
      • 2020-09-13
      相关资源
      最近更新 更多