Merge pull request #6908 from annando/sig-auth

Enable remote authentication with HTTP Signatures
This commit is contained in:
Hypolite Petovan 2019-03-19 08:28:13 -04:00 committed by GitHub
commit d0c0f0345c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 26 deletions

View file

@ -12,7 +12,9 @@ use Friendica\Core\Config\Cache\ConfigCacheLoader;
use Friendica\Core\Config\Cache\IConfigCache; use Friendica\Core\Config\Cache\IConfigCache;
use Friendica\Core\Config\Configuration; use Friendica\Core\Config\Configuration;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\Model\Profile;
use Friendica\Network\HTTPException\InternalServerErrorException; use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Util\HTTPSignature;
use Friendica\Util\Profiler; use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -1143,6 +1145,13 @@ class App
Core\Worker::executeIfIdle(); Core\Worker::executeIfIdle();
} }
if ($this->getMode()->isNormal()) {
$requester = HTTPSignature::getSigner('', $_SERVER);
if (!empty($requester)) {
Profile::addVisitorCookieForHandle($requester);
}
}
// ZRL // ZRL
if (!empty($_GET['zrl']) && $this->getMode()->isNormal()) { if (!empty($_GET['zrl']) && $this->getMode()->isNormal()) {
$this->query_string = Model\Profile::stripZrls($this->query_string); $this->query_string = Model\Profile::stripZrls($this->query_string);

View file

@ -1083,34 +1083,20 @@ 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 $handle Visitor handle
* * @return array Visitor contact array
* @param string $token
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/ */
public static function openWebAuthInit($token) public static function addVisitorCookieForHandle($handle)
{ {
$a = \get_app(); $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. // Try to find the public contact entry of the visitor.
$cid = Contact::getIdForURL($visitor_handle); $cid = Contact::getIdForURL($handle);
if(!$cid) { if (!$cid) {
Logger::log('owt: unable to finger ' . $visitor_handle, Logger::DEBUG); Logger::log('unable to finger ' . $handle, Logger::DEBUG);
return; return [];
} }
$visitor = DBA::selectFirst('contact', [], ['id' => $cid]); $visitor = DBA::selectFirst('contact', [], ['id' => $cid]);
@ -1133,6 +1119,43 @@ class Profile
$_SESSION['remote'][] = ['cid' => $contact['id'], 'uid' => $contact['uid'], 'url' => $visitor['url']]; $_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 = [ $arr = [
'visitor' => $visitor, 'visitor' => $visitor,
'url' => $a->query_string 'url' => $a->query_string

View file

@ -45,9 +45,6 @@ class Photo extends BaseModule
exit; exit;
} }
/// @todo Add Authentication to enable fetching of non public content
// $requester = HTTPSignature::getSigner('', $_SERVER);
$customsize = 0; $customsize = 0;
$photo = false; $photo = false;
switch($a->argc) { switch($a->argc) {

View file

@ -488,8 +488,10 @@ class HTTPSignature
return false; return false;
} }
$hasGoodSignedContent = false;
// Check the digest when it is part of the signed data // 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); $digest = explode('=', $headers['digest'], 2);
if ($digest[0] === 'SHA-256') { if ($digest[0] === 'SHA-256') {
$hashalg = 'sha256'; $hashalg = 'sha256';
@ -503,6 +505,8 @@ class HTTPSignature
if (!empty($hashalg) && base64_encode(hash($hashalg, $content, true)) != $digest[1]) { if (!empty($hashalg) && base64_encode(hash($hashalg, $content, true)) != $digest[1]) {
return false; return false;
} }
$hasGoodSignedContent = true;
} }
// Check if the signed date field is in an acceptable range // 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."); Logger::log("Header date '" . $headers['date'] . "' is with " . $diff . " seconds out of the 300 second frame. The signature is invalid.");
return false; return false;
} }
$hasGoodSignedContent = true;
} }
// Check the content-length when it is part of the signed data // 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']; return $key['url'];
} }