From fa8137cbb009894a2b1c357a1bb15b049ba99024 Mon Sep 17 00:00:00 2001 From: Alan Hardman Date: Wed, 22 Jan 2020 09:48:44 -0700 Subject: [PATCH 1/5] OAuth 1: Fix special characters in query strings This changes the OAuth 1 HMAC signature generation to support query strings in the path that include special characters, and fixes query strings that use a PHP-style array syntax for the key. For example, `foo[bar]=1` would previously trigger an error, but is now handled correctly, encoding the key to `foo%5Bbar%5D` before hashing. Encoding of query values is changed to use RFC 1738-style plus encoding, then full RFC 3986 encoding, which seems to match most implementations. This at least fixes values with spaces when authenticating against the Magento 2 REST API, and matches how Postman encodes the value in its HMAC process. --- src/OAuth/OAuth1/Signature/Signature.php | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/OAuth/OAuth1/Signature/Signature.php b/src/OAuth/OAuth1/Signature/Signature.php index 2e13eb37..325b6dff 100644 --- a/src/OAuth/OAuth1/Signature/Signature.php +++ b/src/OAuth/OAuth1/Signature/Signature.php @@ -56,13 +56,13 @@ public function setTokenSecret($token) */ public function getSignature(UriInterface $uri, array $params, $method = 'POST') { - parse_str($uri->getQuery(), $queryStringData); + $queryStringData = $this->parseQueryString($uri->getQuery()); foreach (array_merge($queryStringData, $params) as $key => $value) { - $signatureData[rawurlencode($key)] = rawurlencode($value); + $signatureData[rawurlencode($key)] = rawurlencode(strtr($value, ' ', '+')); } - ksort($signatureData); + uksort($signatureData, 'strnatcmp'); // determine base uri $baseUri = $uri->getScheme() . '://' . $uri->getRawAuthority(); @@ -80,6 +80,24 @@ public function getSignature(UriInterface $uri, array $params, $method = 'POST') return base64_encode($this->hash($baseString)); } + /** + * A userland implementation of parse_str that treats arrays as string keys + * + * @param string $query + * + * @return array + */ + protected function parseQueryString($query) + { + $result = []; + $params = explode('&', $query); + foreach ($params as $param) { + $parts = explode('=', $param, 2); + $result[rawurldecode($parts[0])] = urldecode($parts[1]) ?? null; + } + return $result; + } + /** * @param array $signatureData * From 5d5fd7eb94bf90b16feb2a82bccb0a42794b2a59 Mon Sep 17 00:00:00 2001 From: Alan Hardman Date: Wed, 22 Jan 2020 10:20:54 -0700 Subject: [PATCH 2/5] Woops, don't use PHP 7-only functions in a 5.3 lib :P --- src/OAuth/OAuth1/Signature/Signature.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OAuth/OAuth1/Signature/Signature.php b/src/OAuth/OAuth1/Signature/Signature.php index 325b6dff..84bbfb26 100644 --- a/src/OAuth/OAuth1/Signature/Signature.php +++ b/src/OAuth/OAuth1/Signature/Signature.php @@ -93,7 +93,7 @@ protected function parseQueryString($query) $params = explode('&', $query); foreach ($params as $param) { $parts = explode('=', $param, 2); - $result[rawurldecode($parts[0])] = urldecode($parts[1]) ?? null; + $result[rawurldecode($parts[0])] = urldecode($parts[1]) ?: null; } return $result; } From 3804efe2d523e5815adbd296c372aa0ed2386d97 Mon Sep 17 00:00:00 2001 From: Alan Hardman Date: Wed, 22 Jan 2020 10:27:08 -0700 Subject: [PATCH 3/5] Correctly handle null parameters --- src/OAuth/OAuth1/Signature/Signature.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OAuth/OAuth1/Signature/Signature.php b/src/OAuth/OAuth1/Signature/Signature.php index 84bbfb26..f03c051f 100644 --- a/src/OAuth/OAuth1/Signature/Signature.php +++ b/src/OAuth/OAuth1/Signature/Signature.php @@ -93,7 +93,7 @@ protected function parseQueryString($query) $params = explode('&', $query); foreach ($params as $param) { $parts = explode('=', $param, 2); - $result[rawurldecode($parts[0])] = urldecode($parts[1]) ?: null; + $result[rawurldecode($parts[0])] = isset($parts[1]) ? urldecode($parts[1]) : ''; } return $result; } From 959fa1b9cf0dd008b2d3ec9832039bea65bad0c7 Mon Sep 17 00:00:00 2001 From: Alan Hardman Date: Thu, 6 Feb 2020 16:35:07 -0700 Subject: [PATCH 4/5] Fix empty param handling --- src/OAuth/OAuth1/Signature/Signature.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/OAuth/OAuth1/Signature/Signature.php b/src/OAuth/OAuth1/Signature/Signature.php index f03c051f..25155a1f 100644 --- a/src/OAuth/OAuth1/Signature/Signature.php +++ b/src/OAuth/OAuth1/Signature/Signature.php @@ -92,6 +92,9 @@ protected function parseQueryString($query) $result = []; $params = explode('&', $query); foreach ($params as $param) { + if (!$param) { + continue; + } $parts = explode('=', $param, 2); $result[rawurldecode($parts[0])] = isset($parts[1]) ? urldecode($parts[1]) : ''; } From 5743e3265f1a5b729d5970f594b3367ad5164e30 Mon Sep 17 00:00:00 2001 From: Alan Hardman Date: Thu, 6 Feb 2020 16:36:02 -0700 Subject: [PATCH 5/5] Better empty param handling This allows things like ?0 to still work --- src/OAuth/OAuth1/Signature/Signature.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OAuth/OAuth1/Signature/Signature.php b/src/OAuth/OAuth1/Signature/Signature.php index 25155a1f..06b0d417 100644 --- a/src/OAuth/OAuth1/Signature/Signature.php +++ b/src/OAuth/OAuth1/Signature/Signature.php @@ -92,7 +92,7 @@ protected function parseQueryString($query) $result = []; $params = explode('&', $query); foreach ($params as $param) { - if (!$param) { + if ($param === '') { continue; } $parts = explode('=', $param, 2);