Skip to content

Instantly share code, notes, and snippets.

@daigo75
Forked from linc/gist:700805
Last active December 14, 2015 20:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daigo75/5141929 to your computer and use it in GitHub Desktop.
Save daigo75/5141929 to your computer and use it in GitHub Desktop.
<?php
/**
* @copyright Vanilla Forums Inc.
* @license GNU GPL2
*/
/**
* Instantiating this class will store current user's ID from cookie as $this->UserID.
*
* Usage
* $VanillaIdentity = new VanillaIdentity();
* $LoggedUserID = $VanillaIdentity->GetLoggerUserID();
*
* Please read class documentation to see what options can be passed to its
* constructor.
*
* @see VanillaIdentity::__construct()
*/
class VanillaIdentity {
# Copy these from Vanilla config
// @var string The name of the cookie used by Vanilla
private $CookieName;
// @var string The salt used to hash the cookie
private $CookieSalt;
// @var string The hash method used to hash the cookie
private $CookieHashMethod;
// @var int The User ID retrieved from the cookies
public $UserID = 0;
// @var array Contains a list of the hash methods supported by the class
protected $ValidHashMethods = array(
'md5',
'sha1',
);
// @var int User ID that indicates "No User"
const NO_USER_ID = 0;
const DEFAULT_COOKIE_NAME = 'Vanilla';
const DEFAULT_COOKIE_SALT = '';
const DEFAULT_COOKIE_HASH_METHOD = 'md5';
/**
* Class constructor.
*
* @param string CookieName The name of the cookie used by Vanilla.
* @param string CookieSalt The salt used to hash the cookie.
* @param string $HashMethod The hashing method to use on $Data. Options are MD5 or SHA1.
*/
public function __construct($CookieName = self::DEFAULT_COOKIE_NAME, $CookieSalt = self::DEFAULT_COOKIE_SALT,
$CookieHashMethod = self::DEFAULT_COOKIE_HASH_METHOD) {
$CookieHashMethod = strtolower($CookieHashMethod);
if(!InArrayI($CookieHashMethod, $this->ValidHashMethods)) {
throw new InvalidArgumentException(sprintf('Invalid hash method: "%s". Valid methods are %s.'),
$CookieHashMethod,
implode(', ', $this->ValidHashMethods));
}
$this->CookieName = $CookieName;
$this->CookieSalt = $CookieSalt;
$this->CookieHashMethod = $CookieHashMethod;
}
/**
* 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 The ID of the User currently logged on Vanilla Forums, or zero
* if he is not logged in.
*/
public function GetLoggedUserID() {
if (!$this->_CheckCookie($this->CookieName)) {
return self::NO_USER_ID;
}
list($UserID, $Expiration) = $this->GetCookiePayload($this->CookieName);
// TODO Replace magic number "-2" with constant explaining what it means
// allow for handshake special id
if (!is_numeric($UserID) || $UserID < -2) {
$this->UserID = self::NO_USER_ID;
}
else{
$this->UserID = $UserID;
}
return $this->UserID;
}
protected function GetCookiePayload($CookieName) {
if (!$this->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 $this->CheckCookie($CookieName);
}
protected 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);
// VanillaIdentity::_Hash() just calls VanillaIdentity::__HashHMAC(). Why
// not just using the latter?
$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.
* @param string $CookieHashMethod The hashing method to use on $Data. Options are MD5 or SHA1.
* @param string $CookieSalt The salt to use when hashing the data.
* @return string The hash of the data.
*
*/
protected static function _Hash($Data, $CookieHashMethod, $CookieSalt) {
return self::_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.
* @return string The hash of 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)));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment