CVE-2016-15015: Observable Discrepancy (CWE-203)
value: A vulnerability, which was classified as problematic, was found in viafintech Barzahlen Payment Module PHP SDK up to 2.0.0. Affected is the function verify of the file src/Webhook.php. The manipulation leads to observable timing discrepancy. The complexity of an attack is rather high. The exploitability is told to be difficult. Upgrading to version 2.0.1 is able to address this issue. The patch is identified as 3e7d29dc0ca6c054a6d6e211f32dae89078594c1. It is recommended to upgrade the affected component. VDB-217650 is the identifier assigned to this vulnerability.
CWE description: The product behaves differently or sends different responses under different circumstances in a way that is observable to an unauthorized actor, which exposes security-relevant information about the state of the product, such as whether a particular operation was successful or not.
CVSS v3 base score: 5.3
Published: 2023-01-08T18:15Z
Repository: https://github.com/viafintech/Barzahlen-PHP
Language: PHP
Files changed: src/Middleware.php, src/Webhook.php, tests/MiddlewareTest.php
Commit date: 2016-08-08 20:29:51 +0200
Commit message: Fixed timing attack vulnerability

Diff: diff --git a/src/Middleware.php b/src/Middleware.php
index ff3e7ec..ba981bc 100644
--- a/src/Middleware.php
+++ b/src/Middleware.php
@@ -28,6 +28,32 @@ class Middleware
         );
         $signatureString = implode("\n", $signatureData);
 
         return hash_hmac('sha256', $signatureString, $key);
     }
+
+    /**
+     * @param string $first
+     * @param string $second
+     * @return boolean
+     *
+     * Workaround for PHP < 5.6 by: asphp at dsgml dot com
+     * Source: https://php.net/manual/en/function.hash-equals.php#115635
+     */
+    public static function stringsEqual($first, $second)
+    {
+        if (function_exists('hash_equals')) {
+            return hash_equals($first, $second);
+        }
+
+        if (strlen($first) != strlen($second)) {
+            return false;
+        }
+
+        $res = $first ^ $second;
+        $ret = 0;
+        for ($i = strlen($res) - 1; $i >= 0; $i--) {
+            $ret |= ord($res[$i]);
+        }
+        return !$ret;
+    }
 }
diff --git a/src/Webhook.php b/src/Webhook.php
index 556a326..a862814 100644
--- a/src/Webhook.php
+++ b/src/Webhook.php
@@ -34,8 +34,8 @@ class Webhook
             '',
             $body,
             $this->paymentKey
         );
 
-        return $header['HTTP_BZ_SIGNATURE'] == 'BZ1-HMAC-SHA256 ' . $signature;
+        return Middleware::stringsEqual($header['HTTP_BZ_SIGNATURE'], 'BZ1-HMAC-SHA256 ' . $signature);
     }
 }
\ No newline at end of file
diff --git a/tests/MiddlewareTest.php b/tests/MiddlewareTest.php
index e77f1f3..bc4d68b 100644
--- a/tests/MiddlewareTest.php
+++ b/tests/MiddlewareTest.php
@@ -19,6 +19,30 @@ class MiddlewareTest extends \PHPUnit_Framework_TestCase
             PAYMENTKEY
         );
 
         $this->assertEquals('35764655afcf2121602a5493b58020d3b6b9d75b4150c7395acf6114ae0ba49c', $signature);
     }
+
+    public function testStringsEqualInvalidLength()
+    {
+        $first = 'thisisarandomstring123';
+        $second = 'thisisanotherrandomstring123';
+
+        $this->assertFalse(Middleware::stringsEqual($first, $second));
+    }
+
+    public function testStringsEqualInvalidContent()
+    {
+        $first = 'thisisarandomstring123';
+        $second = 'thisisarandomstring124';
+
+        $this->assertFalse(Middleware::stringsEqual($first, $second));
+    }
+
+    public function testStringsEqualValid()
+    {
+        $first = 'thismustbeavalidhash';
+        $second = 'thismustbeavalidhash';
+
+        $this->assertTrue(Middleware::stringsEqual($first, $second));
+    }
 }

Vulnerable code:         return $header['HTTP_BZ_SIGNATURE'] == 'BZ1-HMAC-SHA256 ' . $signature;
Fixed code: 
    /**
     * @param string $first
     * @param string $second
     * @return boolean
     *
     * Workaround for PHP < 5.6 by: asphp at dsgml dot com
     * Source: https://php.net/manual/en/function.hash-equals.php#115635
     */
    public static function stringsEqual($first, $second)
    {
        if (function_exists('hash_equals')) {
            return hash_equals($first, $second);
        }

        if (strlen($first) != strlen($second)) {
            return false;
        }

        $res = $first ^ $second;
        $ret = 0;
        for ($i = strlen($res) - 1; $i >= 0; $i--) {
            $ret |= ord($res[$i]);
        }
        return !$ret;
    }
        return Middleware::stringsEqual($header['HTTP_BZ_SIGNATURE'], 'BZ1-HMAC-SHA256 ' . $signature);

    public function testStringsEqualInvalidLength()
    {
        $first = 'thisisarandomstring123';
        $second = 'thisisanotherrandomstring123';

        $this->assertFalse(Middleware::stringsEqual($first, $second));
    }

    public function testStringsEqualInvalidContent()
    {
        $first = 'thisisarandomstring123';
        $second = 'thisisarandomstring124';

        $this->assertFalse(Middleware::stringsEqual($first, $second));
    }

    public function testStringsEqualValid()
    {
        $first = 'thismustbeavalidhash';
        $second = 'thismustbeavalidhash';

        $this->assertTrue(Middleware::stringsEqual($first, $second));
    }