29
0
mirror of https://github.com/joomla/joomla-cms.git synced 2024-06-16 09:02:52 +00:00

Fix/38233 webauth preload fido (#38664)

* Use cached FIDO metadata

Places a copy of fido.jwt in the plugin's directory

* Use cached FIDO metadata

Use the cached FIDO metadata in the plugin

* Use cached FIDO metadata

Remove unused local file fallback

* Use cached FIDO metadata

Simplify

* Use cached FIDO metadata

Update Joomla build script

* Use cached FIDO metadata

Let's not break the build if the FIDO server is down

* Use cached FIDO metadata

Do not include the fido.jwt in the repo

* Use cached FIDO metadata

Make sure not to re-fetch the fido.jwt file if it already exists.

* Use cached FIDO metadata

Move the check for the fido.jwt file right after composer install, do not try to fetch the file twice

* Use cached FIDO metadata

Update lang string

* Use cached FIDO metadata

Set the attestation support to disabled by default

* Update deleted files to PR #38664

Co-authored-by: Richard Fath <richard67@users.noreply.github.com>
This commit is contained in:
Nicholas K. Dionysopoulos 2022-09-02 22:37:25 +03:00 committed by GitHub
parent 1c4fcf60b6
commit 205c7a5f7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 94 additions and 66 deletions

3
.gitignore vendored
View File

@ -101,3 +101,6 @@ RoboFile.ini
# Media Manager
/media/com_media/js/mediamanager.min.js.map
/media/com_media/css/mediamanager.min.css.map
# WebAuthn FIDO metadata cache
/plugins/system/webauthn/fido.jwt

View File

@ -6450,6 +6450,8 @@ class JoomlaInstallerScript
'/media/vendor/hotkeys.js/js/hotkeys.min.js',
'/media/vendor/hotkeys.js/js/hotkeys.min.js.gz',
'/media/vendor/hotkeys.js/LICENSE',
// From 4.2.1 to 4.2.2
'/administrator/cache/fido.jwt',
);
$folders = array(

View File

@ -24,7 +24,7 @@ PLG_SYSTEM_WEBAUTHN_ERR_NO_BROWSER_SUPPORT="Sorry, your browser does not support
PLG_SYSTEM_WEBAUTHN_ERR_NO_STORED_CREDENTIAL="Cannot find the stored credentials for your login authenticator."
PLG_SYSTEM_WEBAUTHN_ERR_USER_REMOVED="The user for this authenticator seems to no longer exist on this site."
PLG_SYSTEM_WEBAUTHN_ERR_XHR_INITCREATE="Cannot get the authenticator registration information from your site."
PLG_SYSTEM_WEBAUTHN_FIELD_ATTESTATION_SUPPORT_DESC="Only allow authenticators with verifiable cryptographic signatures to be used for WebAuthn logins. Strongly recommended for high security environments. Requires your site to be able to access <code>https://mds.fidoalliance.org/</code> directly, a writeable cache directory, the system temporary directory being writeable by PHP, and the OpenSSL extension. May prevent some cheaper, non-certified authenticators from working at all. Disabling it also prevents Joomla from identifying the make and model of the authenticator you are using (no icon will be displayed next to the Authenticator Name).<br/><strong>Pro tip:</strong> If you are behind a firewall you can place the data downloaded from <a href=\"https://mds.fidoalliance.org/\" target=\"_blank\" rel=\"noopener noreferrer\">the FIDO Alliance</a> into the file <code>administrator/cache/fido.jwt</code> for this feature to work properly."
PLG_SYSTEM_WEBAUTHN_FIELD_ATTESTATION_SUPPORT_DESC="Only allow authenticators with verifiable cryptographic signatures to be used for WebAuthn logins. Strongly recommended for high security environments. Requires the system temporary directory being writeable by PHP, and the OpenSSL extension. May prevent some cheaper, non-certified authenticators from working at all. Disabling it also prevents Joomla from identifying the make and model of the authenticator you are using (no icon will be displayed next to the Authenticator Name)."
PLG_SYSTEM_WEBAUTHN_FIELD_ATTESTATION_SUPPORT_LABEL="Attestation Support"
PLG_SYSTEM_WEBAUTHN_FIELD_DESC="Lets you manage passwordless login methods using the W3C Web Authentication standard. You need a supported browser and authenticator (eg Google Chrome or Firefox with a FIDO2 certified security key).<br><br><strong>MacOS/iOS/watchOS:</strong> Touch/Face ID.<br><strong>Windows:</strong> Hello (Fingerprint / Facial Recognition / PIN).<br><strong>Android:</strong> Biometric screen lock.<br><br>You can find more details in the <a href=\"https://docs.joomla.org/Special:MyLanguage/WebAuthn_Passwordless_Login\" target=\"_blank\" rel=\"noopener noreferrer\">WebAuthn Passwordless Login documentation</a>."
PLG_SYSTEM_WEBAUTHN_FIELD_LABEL="W3C Web Authentication (WebAuthn) Login"

View File

@ -270,6 +270,14 @@ if ($composerReturnCode !== 0) {
exit(1);
}
// Try to update the fido.jwt file
if (!file_exists(rtrim($fullpath, '\\/') . '/plugins/system/webauthn/fido.jwt'))
{
echo "The file plugins/system/webauthn/fido.jwt was not created. Build failed.\n";
exit (1);
}
system('npm install --unsafe-perm', $npmReturnCode);
if ($npmReturnCode !== 0) {

View File

@ -0,0 +1,55 @@
<?php
/**
* @package Joomla.Build
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*
* @phpcs :disable PSR1.Classes.ClassDeclaration.MissingNamespace
*/
echo <<< TEXT
Update FIDO Cache version 1.0
Distributed under the GNU General Public License version 2, or at your option
any later version published by the Free Software Foundation.
TEXT;
if (!isset($fullPath))
{
$fullPath = dirname(__DIR__);
}
$filePath = rtrim($fullPath, '\\/') . '/plugins/system/webauthn/fido.jwt';
if (is_file($filePath) && filemtime($filePath) > (time() - 864000))
{
echo "The file $filePath already exists and is current; nothing to do.\n";
exit (0);
}
echo "Fetching FIDO metadata statements...\n";
$context = stream_context_create([
'http' => [
'method' => 'GET',
'follow_location' => 1,
'timeout' => 5.0,
]
]);
$rawJwt = @file_get_contents('https://mds.fidoalliance.org/', false, $context);
if ($rawJwt === false) {
echo "Could not get an updated fido.jwt file.\n";
return;
}
echo "Saving JWT file in the plugin directory...\n";
file_put_contents($filePath, $rawJwt);
echo "File saved: $filePath\n";

View File

@ -117,5 +117,10 @@
},
"replace": {
"paragonie/random_compat": "9.99.99"
},
"scripts": {
"post-install-cmd": [
"php build/update_fido_cache.php"
]
}
}

View File

@ -55,7 +55,7 @@ return new class implements ServiceProviderInterface
$metadataRepository = null;
$params = new Joomla\Registry\Registry($config['params'] ?? '{}');
if ($params->get('attestationSupport', 1) == 1) {
if ($params->get('attestationSupport', 0) == 1) {
$metadataRepository = $container->has(MetadataStatementRepository::class)
? $container->get(MetadataStatementRepository::class)
: new MetadataRepository();

View File

@ -133,7 +133,7 @@ final class Webauthn extends CMSPlugin implements SubscriberInterface
], $logLevels, ["webauthn.system"]);
$this->authenticationHelper = $authHelper ?? (new Authentication());
$this->authenticationHelper->setAttestationSupport($this->params->get('attestationSupport', 1) == 1);
$this->authenticationHelper->setAttestationSupport($this->params->get('attestationSupport', 0) == 1);
}
/**

View File

@ -52,14 +52,12 @@ final class MetadataRepository implements MetadataStatementRepository
private $mdsMap = [];
/**
* Public constructor.
* Have I already tried to load the metadata cache?
*
* @since 4.2.0
* @var bool
* @since __DEPLOY_VERSION__
*/
public function __construct()
{
$this->load();
}
private $loaded = false;
/**
* Find an authenticator metadata statement given an AAGUID
@ -71,6 +69,8 @@ final class MetadataRepository implements MetadataStatementRepository
*/
public function findOneByAAGUID(string $aaguid): ?MetadataStatement
{
$this->load();
$idx = $this->mdsMap[$aaguid] ?? null;
return $idx ? $this->mdsCache[$idx] : null;
@ -84,6 +84,8 @@ final class MetadataRepository implements MetadataStatementRepository
*/
public function getKnownAuthenticators(): array
{
$this->load();
$mapKeys = function (MetadataStatement $meta) {
return $meta->getAaguid();
};
@ -107,56 +109,22 @@ final class MetadataRepository implements MetadataStatementRepository
/**
* Load the authenticator metadata cache
*
* @param bool $force Force reload from the web service
*
* @return void
* @since 4.2.0
*/
private function load(bool $force = false): void
private function load(): void
{
if ($this->loaded) {
return;
}
$this->loaded = true;
$this->mdsCache = [];
$this->mdsMap = [];
$jwtFilename = JPATH_CACHE . '/fido.jwt';
// If the file exists and it's over one month old do retry loading it.
if (file_exists($jwtFilename) && filemtime($jwtFilename) < (time() - 2592000)) {
$force = true;
}
/**
* Try to load the MDS source from the FIDO Alliance and cache it.
*
* We use a short timeout limit to avoid delaying the page load for way too long. If we fail
* to download the file in a reasonable amount of time we write an empty string in the
* file which causes this method to not proceed any further.
*/
if (!file_exists($jwtFilename) || $force) {
// Only try to download anything if we can actually cache it!
if ((file_exists($jwtFilename) && is_writable($jwtFilename)) || (!file_exists($jwtFilename) && is_writable(JPATH_CACHE))) {
$http = HttpFactory::getHttp();
try {
$response = $http->get('https://mds.fidoalliance.org/', [], 5);
$content = ($response->code < 200 || $response->code > 299) ? '' : $response->body;
} catch (\Throwable $e) {
$content = '';
}
}
/**
* If we could not download anything BUT a non-empty file already exists we must NOT
* overwrite it.
*
* This allows, for example, the site owner to manually place the FIDO MDS cache file
* in administrator/cache/fido.jwt. This would be useful for high security sites which
* require attestation BUT are behind a firewall (or disconnected from the Internet),
* therefore cannot download the MDS cache!
*/
if (!empty($content) || !file_exists($jwtFilename) || filesize($jwtFilename) <= 1024) {
file_put_contents($jwtFilename, $content);
}
}
$rawJwt = file_get_contents($jwtFilename);
$jwtFilename = JPATH_PLUGINS . '/system/webauthn/fido.jwt';
$rawJwt = file_get_contents($jwtFilename);
if (!is_string($rawJwt) || strlen($rawJwt) < 1024) {
return;
@ -175,19 +143,6 @@ final class MetadataRepository implements MetadataStatementRepository
unset($rawJwt);
// Do I need to forcibly update the cache? The JWT has the nextUpdate claim to tell us when to do that.
try {
$nextUpdate = new Date($token->claims()->get('nextUpdate', '2020-01-01'));
if (!$force && !$nextUpdate->diff(new Date())->invert) {
$this->load(true);
return;
}
} catch (Exception $e) {
// OK, don't worry if don't know when the next update is.
}
$entriesMapper = function (object $entry) {
try {
$array = json_decode(json_encode($entry->metadataStatement), true);

View File

@ -28,7 +28,7 @@
label="PLG_SYSTEM_WEBAUTHN_FIELD_ATTESTATION_SUPPORT_LABEL"
description="PLG_SYSTEM_WEBAUTHN_FIELD_ATTESTATION_SUPPORT_DESC"
layout="joomla.form.field.radio.switcher"
default="1"
default="0"
filter="integer"
>
<option value="0">JDISABLED</option>