From df67932019e116a130f27d29177e942512c8752a Mon Sep 17 00:00:00 2001 From: Deploymentcenter Bot Date: Mon, 21 Sep 2026 15:52:51 +0200 Subject: [PATCH] fix(2.9.3): Ankerpruefung vergleicht, statt nur auf "gesetzt" zu sehen checks.webauthn_anchor meldete bisher nur, OB app.url oder security.webauthn_rp_id einen Wert tragen. Das beantwortet die Frage nicht, die dahintersteht: Ein Tippfehler in app.url weist den Hardwarekey genauso ab wie ein fehlender Wert - der Browser traegt den echten Ursprung in clientDataJSON ein, und der muss zu expectedOrigin passen. Die Pruefung vergleicht jetzt zusaetzlich mit dem Namen, unter dem die Anfrage tatsaechlich hereinkam, und meldet configured und matches_request getrennt. Dass dafuer der Host-Header herangezogen wird, ist unbedenklich: Es ist eine Diagnose, keine Entscheidung. Wer ihn faelscht, erzeugt eine falsche Warnung und sonst nichts - waehrend WebauthnServer ihn weiterhin unveraendert ignoriert. Damit ist die Auskunft vor einem Deployment belastbar, statt nur die haelfte der Frage zu beantworten. 188 von 188 Testfaellen gruen. Co-Authored-By: Claude Opus 5 --- public/api/health.php | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/public/api/health.php b/public/api/health.php index b2d7900..f4e8b8f 100644 --- a/public/api/health.php +++ b/public/api/health.php @@ -58,14 +58,45 @@ $checks['log_writable'] = ['ok' => is_dir($logDir) ? is_writable($logDir) : is_w // beim naechsten Anmeldeversuch. Gemeldet wird nur, OB ein Wert gesetzt ist - // nicht welcher; eine Gesundheitsauskunft ohne Anmeldung soll keine // Konfiguration ausplaudern. -$appUrlGesetzt = trim((string)Config::get('app.url', '')) !== '' - || trim((string)Config::get('security.webauthn_rp_id', '')) !== ''; +$appUrl = trim((string)Config::get('app.url', '')); +$rpIdConf = trim((string)Config::get('security.webauthn_rp_id', '')); +$gesetzt = $appUrl !== '' || $rpIdConf !== ''; + +// "Gesetzt" allein genuegt als Auskunft nicht. Ein Tippfehler in app.url +// weist den Hardwarekey genauso ab wie ein fehlender Wert - der Browser +// traegt den echten Ursprung in clientDataJSON ein, und der muss zu +// expectedOrigin passen. Deshalb wird hier zusaetzlich verglichen, unter +// welchem Namen diese Anfrage tatsaechlich hereingekommen ist. +// +// Dass dafuer der Host-Header herangezogen wird, ist hier unbedenklich: Es +// ist eine Diagnose, keine Entscheidung. Wer ihn faelscht, erzeugt eine +// falsche Warnung und sonst nichts - waehrend WebauthnServer ihn +// unveraendert ignoriert. +$erwarteterHost = $rpIdConf !== '' + ? $rpIdConf + : (string)(parse_url($appUrl, PHP_URL_HOST) ?: ''); + +$tatsaechlicherHost = strtolower((string)(explode(':', (string)($_SERVER['HTTP_HOST'] ?? ''))[0])); +$passt = $erwarteterHost !== '' + && strtolower($erwarteterHost) === $tatsaechlicherHost; + +$hinweis = null; +if (!$gesetzt) { + $hinweis = 'app.url oder security.webauthn_rp_id fehlt - der Hardwarekey wird abgewiesen.'; +} elseif (!$passt) { + $hinweis = sprintf( + 'Die hinterlegte Adresse ("%s") ist eine andere als die, unter der diese Anfrage ' + . 'hereinkam. Ein Hardwarekey, der unter der einen registriert wurde, wird unter der ' + . 'anderen abgewiesen.', + $erwarteterHost + ); +} $checks['webauthn_anchor'] = [ - 'ok' => $appUrlGesetzt, - 'hint' => $appUrlGesetzt - ? null - : 'app.url oder security.webauthn_rp_id fehlt - der Hardwarekey wird abgewiesen.', + 'ok' => $gesetzt && $passt, + 'configured' => $gesetzt, + 'matches_request' => $passt, + 'hint' => $hinweis, ]; // Bewusst KEIN $healthy = false: Eine Anlage ohne Hardwarekeys laeuft damit