【问题标题】:Authenticating a user with Cookies using Vanilla Forums使用 Vanilla 论坛使用 Cookie 对用户进行身份验证
【发布时间】:2013-02-23 23:43:27
【问题描述】:

我正在我的网站 mydomain.com/forums 上运行一个论坛,它使用 Vanilla 框架。

但是,我希望在该论坛上注册的用户能够将 cmets 留在我网站的其他页面上,即 mydomain.com/blog。因此,我需要一种方法来检查用户是否已登录,或者如果没有,则为他们提供 Vanilla 登录框。

我的原版代码根本不在我的博客页面上,所以我需要一些额外的脚本。经过大量挖掘,我在网上找到了这个https://gist.github.com/lincolnwebs/700805,如果已登录,则为 user_id,如果未登录,则为 0。所以它看起来效果很好,因为我不必包含整个 vanilla 框架。

脚本似乎正在检查用户浏览器上 cookie 的值。有人可以伪造 cookie 的价值并获得对某人帐户的访问权限吗?

作为一个相对的新手,有人可以解释一下这是否是一种安全可靠的方式来验证用户是否已登录?我花了很长时间才找到这个,它似乎被隐藏起来/没有公开。此外,该脚本并非 100% 完美,因为它在静态函数中使用了 $this。

谢谢

<?php
/**
 * @copyright Vanilla Forums Inc.
 * @license GNU GPL2
 */

/**
 * Instantiating this class will store current user's ID from cookie as $this->UserID.
 */
class VanillaIdentity {

   # Copy these from Vanilla config
   public $CookieName = 'Vanilla';
   public $CookieSalt = ''; 
   public $CookieHashMethod = 'md5';
   public $UserID = 0;

   /**
    * Returns the unique id assigned to the user in the database (retrieved
    * from the session cookie if the cookie authenticates) or FALSE if not
    * found or authentication fails.
    *
    * @return int
    */
   public function __construct() {         
      if (!$this->_CheckCookie($this->CookieName)) return 0;

      list($UserID, $Expiration) = $this->GetCookiePayload($this->CookieName);

      if (!is_numeric($UserID) || $UserID < -2) // allow for handshake special id
         $this->UserID = 0;
      else
         $this->UserID = $UserID;
   }

   public static function GetCookiePayload($CookieName) {
      if (!self::CheckCookie($CookieName)) return FALSE;

      $Payload = explode('|', $_COOKIE[$CookieName]);

      // Get rid of check fields like HashKey, HMAC and Time
      array_shift($Payload);
      array_shift($Payload);
      array_shift($Payload);

      return $Payload;
   }

   protected function _CheckCookie($CookieName) {
      return self::CheckCookie($CookieName);
   }

   public static function CheckCookie($CookieName) {

      if (empty($_COOKIE[$CookieName])) {
         return FALSE;
      }

      $CookieHashMethod = $this->CookieHashMethod;
      $CookieSalt = $this->CookieSalt;

      $CookieData = explode('|', $_COOKIE[$CookieName]);
      if (count($CookieData) < 5) {
         return FALSE;
      }

      list($HashKey, $CookieHash, $Time, $UserID, $Expiration) = $CookieData;
      if ($Expiration < time() && $Expiration != 0) {
         return FALSE;
      }

      $Key = self::_Hash($HashKey, $CookieHashMethod, $CookieSalt);
      $GeneratedHash = self::_HashHMAC($CookieHashMethod, $HashKey, $Key);

      if ($CookieHash != $GeneratedHash) {
         return FALSE;
      }

      return TRUE;
   }

   /**
    * Returns $this->_HashHMAC with the provided data, the default hashing method
    * (md5), and the server's COOKIE.SALT string as the key.
    *
    * @param string $Data The data to place in the hash.
    */
   protected static function _Hash($Data, $CookieHashMethod, $CookieSalt) {
      return Gdn_CookieIdentity::_HashHMAC($CookieHashMethod, $Data, $CookieSalt);
   }

   /**
    * Returns the provided data hashed with the specified method using the
    * specified key.
    *
    * @param string $HashMethod The hashing method to use on $Data. Options are MD5 or SHA1.
    * @param string $Data The data to place in the hash.
    * @param string $Key The key to use when hashing the data.
    */
   protected static function _HashHMAC($HashMethod, $Data, $Key) {
      $PackFormats = array('md5' => 'H32', 'sha1' => 'H40');

      if (!isset($PackFormats[$HashMethod]))
         return false;

      $PackFormat = $PackFormats[$HashMethod];
      // this is the equivalent of "strlen($Key) > 64":
      if (isset($Key[63]))
         $Key = pack($PackFormat, $HashMethod($Key));
      else
         $Key = str_pad($Key, 64, chr(0));

      $InnerPad = (substr($Key, 0, 64) ^ str_repeat(chr(0x36), 64));
      $OuterPad = (substr($Key, 0, 64) ^ str_repeat(chr(0x5C), 64));

      return $HashMethod($OuterPad . pack($PackFormat, $HashMethod($InnerPad . $Data)));
   }

}

【问题讨论】:

  • mydomain.com/blog 的认证使用什么框架?
  • 这基本上是问题的症结所在。我目前没有对博客进行任何身份验证,但我想使用 Vanilla 的用户管理软件,所以我想使用 Vanilla 对我的论坛之外的用户进行身份验证。

标签: authentication cookies


【解决方案1】:

我不是 Vanilla 和 JavaScript 专家。我非常了解 HTTP 身份验证和 SSO,我将尝试澄清您的几个问题。

首先,在 HTTP 身份验证之后,应用程序将用户 cookie 设置到浏览器。 在下一次请求时,浏览器会向服务器发送一个 cookie。请注意,在设置 cookie 期间使用什么路径很重要。如果路径是“/”,则浏览器将 cookie 发送到服务器上的所有应用程序。

cookie 可以被劫持并用于访问应用程序。请注意,黑客可能无法解密 cookie - 只需使用它即可。有关详细信息,请参阅以下链接:https://www.owasp.org/index.php/Session_hijacking_attack。为了防止会话劫持,您可以使用 HTTPS。

要解决您的问题,您需要 1) 对 mydomain.com/forums 和 mydomain.com/blog 使用相同的用户存储库 在这种情况下,用户“bob”将是两个应用程序的同一用户。 2) 确保 Vanilla 使用路径“/”设置 cookie 在这种情况下,浏览器会同时为 mydomain.com/forums 和 mydomain.com/blog 发送 cookie。

我找到了以下 Vanilla 插件——它可能会彻底解决您的问题: http://vanillaforums.org/page/SingleSignOn

希望对您有所帮助。如果您需要任何其他说明,请发表评论。

【讨论】:

  • 同意。此外,即使您可以在不解密 cookie 的情况下劫持会话,您也必须对其进行加密以防止其他攻击。 VanillaIdentity 和 SingleSingOn(jsConnect) 都使用 md5(默认)或 sha1。两者都是broken。因此,您可以调整库以使用 scrypt 或 bcrypt,或者将它们配置为使用带有良好盐的 sha1 并希望获得最好的结果。
  • 感谢您的回答。使用 scrpt 或 bcrypt 劫持 cookie 是否安全?
  • 保护 cookie 劫持最安全的方法是使用 HTTPS
猜你喜欢
  • 2015-02-21
  • 2020-12-04
  • 2020-08-04
  • 1970-01-01
  • 2019-01-10
  • 2021-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多