File size: 3,395 Bytes
e4f4821 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
<?php
namespace Kanboard\Core\Security;
use Kanboard\Core\Base;
/**
* Token Handler
*
* @package security
* @author Frederic Guillot
*/
class Token extends Base
{
protected static $KEY_LENGTH = 32;
protected static $NONCE_LENGTH = 16;
protected static $HMAC_ALGO = 'sha256';
protected static $HMAC_LENGTH = 16;
/**
* Generate a random token with different methods: openssl or /dev/urandom or fallback to uniqid()
*
* @static
* @access public
* @return string Random token
*/
public static function getToken($length = 30)
{
return bin2hex(random_bytes($length));
}
/**
* Generate and store a one-time CSRF token
*
* @access public
* @return string Random token
*/
public function getCSRFToken()
{
return $this->createSessionToken('csrf');
}
/**
* Generate and store a reusable CSRF token
*
* @access public
* @return string
*/
public function getReusableCSRFToken()
{
return $this->createSessionToken('pcsrf');
}
/**
* Check if the token exists for the current session (a token can be used only one time)
*
* @access public
* @param string $token CSRF token
* @return bool
*/
public function validateCSRFToken($token)
{
return $this->validateSessionToken('csrf', $token);
}
/**
* Check if the token exists as a reusable CSRF token
*
* @access public
* @param string $token CSRF token
* @return bool
*/
public function validateReusableCSRFToken($token)
{
return $this->validateSessionToken('pcsrf', $token);
}
/**
* Generate a session token of the given type
*
* @access protected
* @param string $type Token type
* @return string Random token
*/
protected function createSessionToken($type)
{
$nonce = self::getToken(self::$NONCE_LENGTH);
return $nonce . $this->signSessionToken($type, $nonce);
}
/**
* Check a session token of the given type
*
* @access protected
* @param string $type Token type
* @param string $token Session token
* @return bool
*/
protected function validateSessionToken($type, $token)
{
if (!is_string($token)) {
return false;
}
if (strlen($token) != (self::$NONCE_LENGTH + self::$HMAC_LENGTH) * 2) {
return false;
}
$nonce = substr($token, 0, self::$NONCE_LENGTH * 2);
$hmac = substr($token, self::$NONCE_LENGTH * 2, self::$HMAC_LENGTH * 2);
return hash_equals($this->signSessionToken($type, $nonce), $hmac);
}
/**
* Sign a nonce with the key belonging to the given type
*
* @access protected
* @param string $type Token type
* @param string $nonce Nonce to sign
* @return string
*/
protected function signSessionToken($type, $nonce)
{
if (!session_exists($type . '_key')) {
session_set($type . '_key', self::getToken(self::$KEY_LENGTH));
}
$data = $nonce . '-' . session_id();
$key = session_get($type . '_key');
$hmac = hash_hmac(self::$HMAC_ALGO, $data, $key, true);
return bin2hex(substr($hmac, 0, self::$HMAC_LENGTH));
}
}
|