Skip to content

Commit

Permalink
Allow nonce checks to provide custom required referrer URL. (matomo-o…
Browse files Browse the repository at this point in the history
…rg#17228)

* Allow checking nonce against custom referrer if needed.

* Add tests.
  • Loading branch information
diosmosis committed Feb 17, 2021
1 parent 4196412 commit 715ded1
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 4 deletions.
23 changes: 19 additions & 4 deletions core/Nonce.php
Expand Up @@ -65,9 +65,10 @@ public static function getNonce($id, $ttl = 600)
*
* @param string $id The nonce's unique ID. See {@link getNonce()}.
* @param string $cnonce Nonce sent from client.
* @param string $expectedReferrerHost The expected referrer host for the HTTP referrer URL.
* @return bool `true` if valid; `false` otherwise.
*/
public static function verifyNonce($id, $cnonce)
public static function verifyNonce($id, $cnonce, $expectedReferrerHost = null)
{
$ns = new SessionNamespace($id);
$nonce = $ns->nonce;
Expand All @@ -79,7 +80,10 @@ public static function verifyNonce($id, $cnonce)

// validate referrer
$referrer = Url::getReferrer();
if (!empty($referrer) && !Url::isLocalUrl($referrer)) {
if (empty($expectedReferrerHost) && !empty($referrer) && !Url::isLocalUrl($referrer)) {
return false;
}
if (!empty($expectedReferrerHost) && !self::isReferrerHostValid($referrer, $expectedReferrerHost)) {
return false;
}

Expand All @@ -95,6 +99,17 @@ public static function verifyNonce($id, $cnonce)
return true;
}

// public for tests
public static function isReferrerHostValid($referrer, $expectedReferrerHost)
{
if (empty($referrer)) {
return false;
}

$referrerHost = Url::getHostFromUrl($referrer);
return preg_match('/(^|\.)' . preg_quote($expectedReferrerHost) . '$/i', $referrerHost);
}

/**
* Force expiration of the current nonce.
*
Expand Down Expand Up @@ -169,13 +184,13 @@ public static function getAcceptableOrigins()
* **nonce** query parameter is used.
* @throws \Exception if the nonce is invalid. See {@link verifyNonce()}.
*/
public static function checkNonce($nonceName, $nonce = null)
public static function checkNonce($nonceName, $nonce = null, $expectedReferrerHost = null)
{
if ($nonce === null) {
$nonce = Common::getRequestVar('nonce', null, 'string');
}

if (!self::verifyNonce($nonceName, $nonce)) {
if (!self::verifyNonce($nonceName, $nonce, $expectedReferrerHost)) {
throw new \Exception(Piwik::translate('General_ExceptionNonceMismatch'));
}

Expand Down
23 changes: 23 additions & 0 deletions tests/PHPUnit/Unit/NonceTest.php
Expand Up @@ -42,4 +42,27 @@ public function test_getAcceptableOrigins($host, $expected)
Config::getInstance()->General['trusted_hosts'] = array('example.com');
$this->assertEquals($expected, Nonce::getAcceptableOrigins(), $host);
}

/**
* @dataProvider getTestDataForIsReferrerHostValid
* @group Core
*/
public function test_isReferrerHostValid($referrer, $expectedHost, $expectedResult)
{
$result = Nonce::isReferrerHostValid($referrer, $expectedHost);
$this->assertEquals($expectedResult, $result);
}

public function getTestDataForIsReferrerHostValid()
{
return [
['http://referrer.com', 'someotherreferrer.com', false],
['http://referrer.com/referrer/path', 'referrer.com', true],
['http://areferrer.com', 'referrer.com', false],
['http://sub.referrer.com', 'referrer.com', true],
['http://sub.referrer.com', 'sub.referrer.com', true],
['http://sub.referrer.com', 'a.sub.referrer.com', false],
['http://sub.referrer.com', 'sub2.referrer.com', false],
];
}
}

0 comments on commit 715ded1

Please sign in to comment.