From 0aab620d320d9c74ca507a15aed7e7d583b260bf Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 19 Mar 2019 06:44:51 +0000 Subject: [PATCH] Enable remote authentication with HTTP Signatures --- src/App.php | 9 +++++ src/Model/Profile.php | 69 +++++++++++++++++++++++++------------- src/Module/Photo.php | 3 -- src/Util/HTTPSignature.php | 13 ++++++- 4 files changed, 66 insertions(+), 28 deletions(-) diff --git a/src/App.php b/src/App.php index b3eed92a0..b5ad68321 100644 --- a/src/App.php +++ b/src/App.php @@ -12,7 +12,9 @@ use Friendica\Core\Config\Cache\ConfigCacheLoader; use Friendica\Core\Config\Cache\IConfigCache; use Friendica\Core\Config\Configuration; use Friendica\Database\DBA; +use Friendica\Model\Profile; use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Util\HTTPSignature; use Friendica\Util\Profiler; use Psr\Log\LoggerInterface; @@ -1143,6 +1145,13 @@ class App Core\Worker::executeIfIdle(); } + if ($this->getMode()->isNormal()) { + $requester = HTTPSignature::getSigner('', $_SERVER); + if (!empty($requester)) { + Profile::addVisitorCookieForHandle($requester); + } + } + // ZRL if (!empty($_GET['zrl']) && $this->getMode()->isNormal()) { $this->query_string = Model\Profile::stripZrls($this->query_string); diff --git a/src/Model/Profile.php b/src/Model/Profile.php index b10e9848f..b85640799 100644 --- a/src/Model/Profile.php +++ b/src/Model/Profile.php @@ -1083,34 +1083,18 @@ class Profile } /** - * OpenWebAuth authentication. + * Set the visitor cookies (see remote_user()) for the given handle * - * Ported from Hubzilla: https://framagit.org/hubzilla/core/blob/master/include/zid.php - * - * @param string $token - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException + * @param string $handle Visitor handle + * @return array Visitor contact array */ - public static function openWebAuthInit($token) + public static function addVisitorCookieForHandle($handle) { - $a = \get_app(); - - // Clean old OpenWebAuthToken entries. - OpenWebAuthToken::purge('owt', '3 MINUTE'); - - // Check if the token we got is the same one - // we have stored in the database. - $visitor_handle = OpenWebAuthToken::getMeta('owt', 0, $token); - - if($visitor_handle === false) { - return; - } - // Try to find the public contact entry of the visitor. - $cid = Contact::getIdForURL($visitor_handle); - if(!$cid) { - Logger::log('owt: unable to finger ' . $visitor_handle, Logger::DEBUG); - return; + $cid = Contact::getIdForURL($handle); + if (!$cid) { + Logger::log('unable to finger ' . $handle, Logger::DEBUG); + return []; } $visitor = DBA::selectFirst('contact', [], ['id' => $cid]); @@ -1133,6 +1117,43 @@ class Profile $_SESSION['remote'][] = ['cid' => $contact['id'], 'uid' => $contact['uid'], 'url' => $visitor['url']]; } + + $a->contact = $visitor; + + Logger::info('Authenticated visitor', ['url' => $visitor['url']]); + + return $visitor; + } + + /** + * OpenWebAuth authentication. + * + * Ported from Hubzilla: https://framagit.org/hubzilla/core/blob/master/include/zid.php + * + * @param string $token + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + public static function openWebAuthInit($token) + { + $a = \get_app(); + + // Clean old OpenWebAuthToken entries. + OpenWebAuthToken::purge('owt', '3 MINUTE'); + + // Check if the token we got is the same one + // we have stored in the database. + $visitor_handle = OpenWebAuthToken::getMeta('owt', 0, $token); + + if ($visitor_handle === false) { + return; + } + + $visitor = self::addVisitorCookieForHandle($visitor_handle); + if (empty($visitor)) { + return; + } + $arr = [ 'visitor' => $visitor, 'url' => $a->query_string diff --git a/src/Module/Photo.php b/src/Module/Photo.php index f5bbf4a77..15ea261fb 100644 --- a/src/Module/Photo.php +++ b/src/Module/Photo.php @@ -45,9 +45,6 @@ class Photo extends BaseModule exit; } - /// @todo Add Authentication to enable fetching of non public content - // $requester = HTTPSignature::getSigner('', $_SERVER); - $customsize = 0; $photo = false; switch($a->argc) { diff --git a/src/Util/HTTPSignature.php b/src/Util/HTTPSignature.php index ba44bcc80..d5e1732c0 100644 --- a/src/Util/HTTPSignature.php +++ b/src/Util/HTTPSignature.php @@ -488,8 +488,10 @@ class HTTPSignature return false; } + $hasGoodSignedContent = false; + // Check the digest when it is part of the signed data - if (in_array('digest', $sig_block['headers'])) { + if (!empty($content) && in_array('digest', $sig_block['headers'])) { $digest = explode('=', $headers['digest'], 2); if ($digest[0] === 'SHA-256') { $hashalg = 'sha256'; @@ -503,6 +505,8 @@ class HTTPSignature if (!empty($hashalg) && base64_encode(hash($hashalg, $content, true)) != $digest[1]) { return false; } + + $hasGoodSignedContent = true; } // Check if the signed date field is in an acceptable range @@ -512,6 +516,7 @@ class HTTPSignature Logger::log("Header date '" . $headers['date'] . "' is with " . $diff . " seconds out of the 300 second frame. The signature is invalid."); return false; } + $hasGoodSignedContent = true; } // Check the content-length when it is part of the signed data @@ -521,6 +526,12 @@ class HTTPSignature } } + // Ensure that the authentication had been done with some content + // Without this check someone could authenticate with fakeable data + if (!$hasGoodSignedContent) { + return false; + } + return $key['url']; }