From d06d9e1e92592a5462ad29359ff634220be9fc63 Mon Sep 17 00:00:00 2001 From: ksvirkou Date: Thu, 11 Aug 2022 17:50:04 +0300 Subject: [PATCH 1/2] add Sinature Util and Webhooks Util is deprecated --- src/Utils/Signature.php | 94 ++++++++++++++++++++++++++++++ src/Utils/Webhooks.php | 4 +- tests/unit/Utils/SignatureTest.php | 45 ++++++++++++++ 3 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 src/Utils/Signature.php create mode 100644 tests/unit/Utils/SignatureTest.php diff --git a/src/Utils/Signature.php b/src/Utils/Signature.php new file mode 100644 index 00000000..2cfbf26b --- /dev/null +++ b/src/Utils/Signature.php @@ -0,0 +1,94 @@ + $options + * @param string $secret['signature'] hubspot signarute + * @param string $secret['secret'] the Secret of your app + * @param string $secret['requestBody'] the request body include the payload of the request as a JSON string + * @param string $secret['httpUri'] the full URL of the incoming request, including the http:// prefix, the path of your endpoint, and any query parameters + * @param string $secret['httpMethod'] the method of the incoming request, such as GET or POST + * @param string $secret['timestamp'] a unix timestamp of the request, provided in the X-HubSpot-Request-Timestamp header (Reject the request if the timestamp is older than 5 minutes) + * @param string $secret['signatureVersion'] signature version (V1, V2 or V3) + * @param bool $secret['checkTimestamp'] check timestamp or not (default value true) + */ + public static function isValid( + array $options + ): bool { + $signatureVersion = static::getOptionOrThrow($options, 'signatureVersion', 'v1'); + + if ('v3' === $signatureVersion && static::getOptionOrThrow($options, 'checkTimestamp', true)) { + $currentTimestamp = Timestamp::getCurrentTimestampWithMilliseconds(); + $timestamp = (int) static::getOptionOrThrow($options, 'timestamp'); + if (($currentTimestamp - $timestamp) > static::MAX_ALLOWED_TIMESTAMP) { + return false; + } + } + + $hash = static::getHashedSignature($signatureVersion, $options); + + return hash_equals(static::getOptionOrThrow($options, 'signature'), $hash); + } + + /** + * Get hashed signature. + * + * @param string $signatureVersion signature version (V1, V2 or V3) + * @param array $options + * @param string $secret['secret'] the Secret of your app + * @param string $secret['requestBody'] the request body include the payload of the request as a JSON string + * @param string $secret['httpUri'] the full URL of the incoming request, including the http:// prefix, the path of your endpoint, and any query parameters + * @param string $secret['httpMethod'] the method of the incoming request, such as GET or POST + * @param string $secret['timestamp'] a unix timestamp of the request, provided in the X-HubSpot-Request-Timestamp header + */ + public static function getHashedSignature( + string $signatureVersion, + array $options + ): string { + switch ($signatureVersion) { + case 'v1': + $sourceString = static::getOptionOrThrow($options, 'secret').static::getOptionOrThrow($options, 'requestBody'); + + return hash('sha256', $sourceString); + + case 'v2': + $sourceString = static::getOptionOrThrow($options, 'secret').static::getOptionOrThrow($options, 'httpMethod').static::getOptionOrThrow($options, 'httpUri').static::getOptionOrThrow($options, 'requestBody'); + + return hash('sha256', $sourceString); + + case 'v3': + $sourceString = static::getOptionOrThrow($options, 'httpMethod').static::getOptionOrThrow($options, 'httpUri').static::getOptionOrThrow($options, 'requestBody').static::getOptionOrThrow($options, 'timestamp'); + + return base64_encode(hash_hmac('sha256', $sourceString, static::getOptionOrThrow($options, 'secret'), true)); + + default: + throw new UnexpectedValueException("Not supported signature version: {$signatureVersion}"); + } + } + + protected static function getOptionOrThrow(array $options, string $name, $default = null) + { + if (array_key_exists($name, $options)) { + return $options[$name]; + } + + if (null !== $default) { + return $default; + } + + throw new UnexpectedValueException("Not provided {$name}"); + } +} diff --git a/src/Utils/Webhooks.php b/src/Utils/Webhooks.php index 932c1a1a..f085110a 100644 --- a/src/Utils/Webhooks.php +++ b/src/Utils/Webhooks.php @@ -6,7 +6,9 @@ class Webhooks { /** * Validation of Hubspot Signature. - * + * + * @deprecated + * * @param string $signature hubspot signarute * @param string $secret the Secret of your app * @param string $requestBody a set of scopes that your app will need access to diff --git a/tests/unit/Utils/SignatureTest.php b/tests/unit/Utils/SignatureTest.php new file mode 100644 index 00000000..570f61ab --- /dev/null +++ b/tests/unit/Utils/SignatureTest.php @@ -0,0 +1,45 @@ + hash('sha256', $this->secret.$this->requestBody), + 'secret' => $this->secret, + 'requestBody' => $this->requestBody + ]); + + $this->assertEquals( + true, + $result + ); + } + + /** @test */ + public function validationHubspotSignatureInvalidData() + { + $result = Utils\Signature::isHubspotSignatureValid([ + 'signature' => hash('sha256', $this->secret.$this->requestBody.'1'), + 'secret' => $this->secret, + 'requestBody' => $this->requestBody + ]); + + $this->assertEquals( + false, + $result + ); + } +} From 0e4a3749c7ff31e05f40291787e8191b402c9a4b Mon Sep 17 00:00:00 2001 From: ksvirkou Date: Thu, 11 Aug 2022 17:56:13 +0300 Subject: [PATCH 2/2] fix tests --- tests/unit/Utils/SignatureTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/Utils/SignatureTest.php b/tests/unit/Utils/SignatureTest.php index 570f61ab..653c1c4b 100644 --- a/tests/unit/Utils/SignatureTest.php +++ b/tests/unit/Utils/SignatureTest.php @@ -16,7 +16,7 @@ class SignatureTest extends \PHPUnit\Framework\TestCase /** @test */ public function validationHubspotSignatureValidData() { - $result = Utils\Signature::isHubspotSignatureValid([ + $result = Utils\Signature::isValid([ 'signature' => hash('sha256', $this->secret.$this->requestBody), 'secret' => $this->secret, 'requestBody' => $this->requestBody @@ -31,7 +31,7 @@ public function validationHubspotSignatureValidData() /** @test */ public function validationHubspotSignatureInvalidData() { - $result = Utils\Signature::isHubspotSignatureValid([ + $result = Utils\Signature::isValid([ 'signature' => hash('sha256', $this->secret.$this->requestBody.'1'), 'secret' => $this->secret, 'requestBody' => $this->requestBody