]> git.immae.eu Git - github/shaarli/Shaarli.git/commitdiff
API: fix JWT signature verification 739/head
authorVirtualTam <virtualtam@flibidi.net>
Wed, 4 Jan 2017 10:41:05 +0000 (11:41 +0100)
committerVirtualTam <virtualtam@flibidi.net>
Wed, 4 Jan 2017 15:59:47 +0000 (16:59 +0100)
Fixes https://github.com/shaarli/Shaarli/issues/737

Added:
- Base64Url utilities

Fixed:
- use URL-safe Base64 encoding/decoding functions
- use byte representations for HMAC digests
- all JWT parts are Base64Url-encoded

See:
- https://en.wikipedia.org/wiki/JSON_Web_Token
- https://tools.ietf.org/html/rfc7519
- https://scotch.io/tutorials/the-anatomy-of-a-json-web-token
- https://jwt.io/introduction/
- https://en.wikipedia.org/wiki/Base64#URL_applications
- https://secure.php.net/manual/en/function.base64-encode.php#103849

Signed-off-by: VirtualTam <virtualtam@flibidi.net>
application/Base64Url.php [new file with mode: 0644]
application/api/ApiUtils.php
composer.json
tests/api/ApiUtilsTest.php

diff --git a/application/Base64Url.php b/application/Base64Url.php
new file mode 100644 (file)
index 0000000..61590e4
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+namespace Shaarli;
+
+
+/**
+ * URL-safe Base64 operations
+ *
+ * @see https://en.wikipedia.org/wiki/Base64#URL_applications
+ */
+class Base64Url
+{
+    /**
+     * Base64Url-encodes data
+     *
+     * @param string $data Data to encode
+     *
+     * @return string Base64Url-encoded data
+     */
+    public static function encode($data) {
+        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
+    }
+
+    /**
+     * Decodes Base64Url-encoded data
+     *
+     * @param string $data Data to decode
+     *
+     * @return string Decoded data
+     */
+    public static function decode($data) {
+        return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
+    }
+}
index fbb1e72f97dfc30c2da4451e96f51d055fea50e0..a419c39669a1a44bde14cb7baf8a3b3471041d50 100644 (file)
@@ -1,13 +1,11 @@
 <?php
-
 namespace Shaarli\Api;
 
+use Shaarli\Base64Url;
 use Shaarli\Api\Exceptions\ApiAuthorizationException;
 
 /**
- * Class ApiUtils
- *
- * Utility functions for the API.
+ * REST API utilities
  */
 class ApiUtils
 {
@@ -26,17 +24,17 @@ class ApiUtils
             throw new ApiAuthorizationException('Malformed JWT token');
         }
 
-        $genSign = hash_hmac('sha512', $parts[0] .'.'. $parts[1], $secret);
+        $genSign = Base64Url::encode(hash_hmac('sha512', $parts[0] .'.'. $parts[1], $secret, true));
         if ($parts[2] != $genSign) {
             throw new ApiAuthorizationException('Invalid JWT signature');
         }
 
-        $header = json_decode(base64_decode($parts[0]));
+        $header = json_decode(Base64Url::decode($parts[0]));
         if ($header === null) {
             throw new ApiAuthorizationException('Invalid JWT header');
         }
 
-        $payload = json_decode(base64_decode($parts[1]));
+        $payload = json_decode(Base64Url::decode($parts[1]));
         if ($payload === null) {
             throw new ApiAuthorizationException('Invalid JWT payload');
         }
index cfbde1a0695ee7a58eb86ee93e340e36baa6e9d4..2fed0df7700bc5211d9e515d9d0b60f9c2c19e3b 100644 (file)
@@ -24,6 +24,7 @@
     },
     "autoload": {
         "psr-4": {
+            "Shaarli\\": "application",
             "Shaarli\\Api\\": "application/api/",
             "Shaarli\\Api\\Controllers\\": "application/api/controllers",
             "Shaarli\\Api\\Exceptions\\": "application/api/exceptions"
index 10da1459a1fbfee77f469ba251ea6036884d3c2d..4b2fa3b2719a289597adad2d42028f49e613c721 100644 (file)
@@ -2,6 +2,9 @@
 
 namespace Shaarli\Api;
 
+use Shaarli\Base64Url;
+
+
 /**
  * Class ApiUtilsTest
  */
@@ -24,14 +27,14 @@ class ApiUtilsTest extends \PHPUnit_Framework_TestCase
      */
     public static function generateValidJwtToken($secret)
     {
-        $header = base64_encode('{
+        $header = Base64Url::encode('{
             "typ": "JWT",
             "alg": "HS512"
         }');
-        $payload = base64_encode('{
+        $payload = Base64Url::encode('{
             "iat": '. time() .'
         }');
-        $signature = hash_hmac('sha512', $header .'.'. $payload , $secret);
+        $signature = Base64Url::encode(hash_hmac('sha512', $header .'.'. $payload , $secret, true));
         return $header .'.'. $payload .'.'. $signature;
     }
 
@@ -46,9 +49,9 @@ class ApiUtilsTest extends \PHPUnit_Framework_TestCase
      */
     public static function generateCustomJwtToken($header, $payload, $secret)
     {
-        $header = base64_encode($header);
-        $payload = base64_encode($payload);
-        $signature = hash_hmac('sha512', $header . '.' . $payload, $secret);
+        $header = Base64Url::encode($header);
+        $payload = Base64Url::encode($payload);
+        $signature = Base64Url::encode(hash_hmac('sha512', $header . '.' . $payload, $secret, true));
         return $header . '.' . $payload . '.' . $signature;
     }