Skip to content

Commit

Permalink
[jan] Add Horde::signUrl() and Horde::verifySignedUrl().
Browse files Browse the repository at this point in the history
  • Loading branch information
yunosh committed Jun 30, 2017
1 parent 3ccbd41 commit 4b1fe4f
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 10 deletions.
101 changes: 99 additions & 2 deletions framework/Core/lib/Horde.php
Expand Up @@ -148,7 +148,104 @@ public static function debug($event = null, $fname = null,
}

/**
* Add a signature + timestamp to a query string and return the signed
* Adds a signature + timestamp to a URL and returns the signed URL.
*
* @since Horde_Core 2.30.0
*
* @param string|Horde_Url $url The URL to sign.
* @param integer $now The timestamp at which to sign. Leave
* blank for generating signatures; specify
* when testing.
*
* @return string|Horde_Url The signed URL.
*/
public static function signUrl($url, $now = null)
{
global $conf;

if (!isset($conf['secret_key'])) {
return $url;
}

if (is_null($now)) {
$now = time();
}

if ($url instanceof Horde_Url) {
$url->setRaw(true)->add(array('_t' => $now, '_h' => ''));
$url->add(
'_h',
Horde_Url::uriB64Encode(
hash_hmac('sha1', $url . '=', $conf['secret_key'], true)
)
);
return $url;
}

if (strpos($url, '?')) {
$url .= '&';
} else {
$url .= '?';
}
$url .= '_t=' . $now . '&_h=';
$url .= Horde_Url::uriB64Encode(
hash_hmac('sha1', $url, $conf['secret_key'], true)
);

return $url;
}

/**
* Verifies a signature and timestamp on a URL.
*
* @since Horde_Core 2.30.0
*
* @param string $data The signed URL.
* @param integer $now The current time (can override for testing).
*
* @return string|boolean The URL stripped off the signature, of false if
* not verified.
*/
public static function verifySignedUrl($data, $now = null)
{
global $conf;

if (is_null($now)) {
$now = time();
}

$pos = strrpos($data, '&_h=');
if ($pos === false) {
return false;
}
$pos += 4;

$url = substr($data, 0, $pos);
$hmac = substr($data, $pos);

if ($hmac != Horde_Url::uriB64Encode(hash_hmac('sha1', $url, $conf['secret_key'], true))) {
return false;
}

// String was not tampered with; now validate timestamp
parse_str(parse_url($url, PHP_URL_QUERY), $values);
if ($values['_t'] + $conf['urls']['hmac_lifetime'] * 60 < $now) {
return false;
}

$pos = strrpos($data, '&_t=');
if ($pos === false) {
$pos = strrpos($data, '?_t=');
}
if ($pos === false) {
return false;
}

return substr($data, 0, $pos);
}

/**
* Adds a signature + timestamp to a query string and returns the signed
* query string.
*
* @param mixed $queryString The query string (or Horde_Url object)
Expand Down Expand Up @@ -182,7 +279,7 @@ public static function signQueryString($queryString, $now = null)
}

/**
* Verify a signature and timestamp on a query string.
* Verifies a signature and timestamp on a query string.
*
* @param string $data The signed query string.
* @param integer $now The current time (can override for testing).
Expand Down
16 changes: 8 additions & 8 deletions framework/Core/package.xml
Expand Up @@ -28,18 +28,18 @@
<email>mrubinsk@horde.org</email>
<active>yes</active>
</developer>
<date>2017-06-22</date>
<date>2017-06-30</date>
<version>
<release>2.29.2</release>
<api>2.29.0</api>
<release>2.30.0</release>
<api>2.30.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://www.horde.org/licenses/lgpl21">LGPL-2.1</license>
<notes>
*
* [jan] Add Horde::signUrl() and Horde::verifySignedUrl().
</notes>
<contents>
<dir baseinstalldir="/" name="/">
Expand Down Expand Up @@ -4474,15 +4474,15 @@
</release>
<release>
<version>
<release>2.29.2</release>
<api>2.29.0</api></version>
<release>2.30.0</release>
<api>2.30.0</api></version>
<stability>
<release>stable</release>
<api>stable</api></stability>
<date>2017-06-22</date>
<date>2017-06-30</date>
<license uri="http://www.horde.org/licenses/lgpl21">LGPL-2.1</license>
<notes>
*
* [jan] Add Horde::signUrl() and Horde::verifySignedUrl().
</notes>
</release>
</changelog>
Expand Down
40 changes: 40 additions & 0 deletions framework/Core/test/Horde/Core/UrlTest.php
Expand Up @@ -409,6 +409,46 @@ public function testSelfUrl()
$GLOBALS['conf']['server']['port'] = 1234;
$this->assertEquals('http://example.com:1234/hordeurl/test/', Horde::selfUrl(false, true, true));
}

public function testSignUrl()
{
$now = 1000000000;
$query = 'foo=42&bar=xyz';
$signedQuery = 'foo=42&bar=xyz&_t=1000000000&_h=Zt9M0io4vBpM2dA2gMpiPiDKTUA';
$url = 'http://www.example.com';
$signedUrl = 'http://www.example.com?_t=1000000000&_h=UcbwFn6pLKHh2U35cK-GHwGT6_Q';
$urlQuery = 'http://www.example.com?hello=world';
$signedUrlQuery = 'http://www.example.com?hello=world&_t=1000000000&_h=_wQyvcO90UF7S2sdhRr-X4rRT9k';

$signed = Horde::signQueryString($query, $now);
$this->assertEquals($query, $signed);

$signed = Horde::signUrl($url, $now);
$this->assertEquals($url, $signed);
$signed = Horde::signUrl($urlQuery, $now);
$this->assertEquals($urlQuery, $signed);

$GLOBALS['conf']['secret_key'] = 'abcdefghijklmnopqrstuvwxyz';
$GLOBALS['conf']['urls']['hmac_lifetime'] = '30';

$signed = Horde::signQueryString($query, $now);
$this->assertEquals($signedQuery, $signed);
$this->assertTrue(Horde::verifySignedQueryString($signedQuery, $now));
$this->assertFalse(Horde::verifySignedQueryString($query, $now));
$this->assertFalse(Horde::verifySignedQueryString($signedQuery, $now + $GLOBALS['conf']['urls']['hmac_lifetime'] * 60 + 1));

$signed = Horde::signUrl($url, $now);
$this->assertEquals($signedUrl, $signed);
$this->assertEquals($url, Horde::verifySignedUrl($signedUrl, $now));
$this->assertFalse(Horde::verifySignedUrl($url, $now));
$this->assertFalse(Horde::verifySignedUrl($signedUrl, $now + $GLOBALS['conf']['urls']['hmac_lifetime'] * 60 + 1));

$signed = Horde::signUrl($urlQuery, $now);
$this->assertEquals($signedUrlQuery, $signed);
$this->assertEquals($urlQuery, Horde::verifySignedUrl($signedUrlQuery, $now));
$this->assertFalse(Horde::verifySignedUrl($urlQuery, $now));
$this->assertFalse(Horde::verifySignedUrl($signedUrlQuery, $now + $GLOBALS['conf']['urls']['hmac_lifetime'] * 60 + 1));
}
}

class Registry
Expand Down

0 comments on commit 4b1fe4f

Please sign in to comment.