Skip to content

Commit 17c8e96

Browse files
committed
Merge pull request firebase#33 from sarciszewski/patch-1
Allow users to lock their app into an algorithm.
2 parents 724b1fc + b9a6b47 commit 17c8e96

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

Authentication/JWT.php

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
class JWT
1717
{
18+
public static $only_method = 'HS256';
19+
1820
public static $methods = array(
1921
'HS256' => array('hash_hmac', 'SHA256'),
2022
'HS512' => array('hash_hmac', 'SHA512'),
@@ -173,6 +175,11 @@ public static function verify($msg, $signature, $key, $method = 'HS256')
173175
if (empty(self::$methods[$method])) {
174176
throw new DomainException('Algorithm not supported');
175177
}
178+
if (self::$only_method === null) {
179+
throw new DomainException('Algorithm not specified');
180+
} elseif ($method !== self::$only_method) {
181+
throw new DomainException('Incorrect algorithm error');
182+
}
176183
list($function, $algo) = self::$methods[$method];
177184
switch($function) {
178185
case 'openssl':
@@ -185,13 +192,16 @@ public static function verify($msg, $signature, $key, $method = 'HS256')
185192
case 'hash_hmac':
186193
default:
187194
$hash = hash_hmac($algo, $msg, $key, true);
188-
$len = min(strlen($signature), strlen($hash));
195+
if (function_exists('hash_equals')) {
196+
return hash_equals($signature, $hash);
197+
}
198+
$len = min(self::safeStrlen($signature), self::safeStrlen($hash));
189199

190200
$status = 0;
191201
for ($i = 0; $i < $len; $i++) {
192202
$status |= (ord($signature[$i]) ^ ord($hash[$i]));
193203
}
194-
$status |= (strlen($signature) ^ strlen($hash));
204+
$status |= (self::safeStrlen($signature) ^ self::safeStrlen($hash));
195205

196206
return ($status === 0);
197207
}
@@ -299,4 +309,36 @@ private static function handleJsonError($errno)
299309
: 'Unknown JSON error: ' . $errno
300310
);
301311
}
312+
313+
/**
314+
* Get the number of bytes in cryptographic strings.
315+
*
316+
* @param string
317+
* @return int
318+
*/
319+
private static function safeStrlen($str)
320+
{
321+
if (function_exists('mb_strlen')) {
322+
return mb_strlen($str, '8bit');
323+
}
324+
return strlen($str);
325+
}
326+
327+
/**
328+
* Set the only allowed method for this server.
329+
*
330+
* @ref https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
331+
*
332+
* @param string $method array index in self::$methods
333+
*
334+
* @return boolean
335+
*/
336+
public static function setOnlyAllowedMethod($method)
337+
{
338+
if (!empty(self::$methods[$method])) {
339+
self::$only_method = $method;
340+
return true;
341+
}
342+
return false;
343+
}
302344
}

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ $token = array(
2727
"nbf" => 1357000000
2828
);
2929

30+
/*
31+
IMPORTANT
32+
HS256 is actually the default, but you should set it explicitly
33+
if you intend to use another strategy (e.g. RS256):
34+
*/
35+
JWT::setOnlyMethodAllowed('HS256');
36+
3037
$jwt = JWT::encode($token, $key);
3138
$decoded = JWT::decode($jwt, $key);
3239

tests/JWTTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public function testRSEncodeDecode()
102102
$privKey = openssl_pkey_new(array('digest_alg' => 'sha256',
103103
'private_key_bits' => 1024,
104104
'private_key_type' => OPENSSL_KEYTYPE_RSA));
105+
JWT::setOnlyAllowedMethod('RS256');
105106
$msg = JWT::encode('abc', $privKey, 'RS256');
106107
$pubKey = openssl_pkey_get_details($privKey);
107108
$pubKey = $pubKey['key'];
@@ -112,6 +113,7 @@ public function testRSEncodeDecode()
112113
public function testKIDChooser()
113114
{
114115
$keys = array('1' => 'my_key', '2' => 'my_key2');
116+
JWT::setOnlyAllowedMethod('HS256');
115117
$msg = JWT::encode('abc', $keys['1'], 'HS256', '1');
116118
$decoded = JWT::decode($msg, $keys, true);
117119
$this->assertEquals($decoded, 'abc');

0 commit comments

Comments
 (0)