From fb4dcf96fde160be2220c927b868c7484915f869 Mon Sep 17 00:00:00 2001 From: Clemens Creutzburg Date: Thu, 23 Jul 2026 10:44:25 +0200 Subject: [PATCH] M9-Plan: SAML/ADFS als Alternative dokumentiert Ergaenzt den Abschnitt, was eine direkte SAML-Anbindung ans ADFS zusaetzlich kosten wuerde - als Entscheidungsgrundlage neben dem beschlossenen OIDC-Weg, nicht als beschlossener Weg. Kernpunkte: SAML braucht ext-dom, ext-simplexml und ext-mbstring sowie Composer; in der aktuellen Dev-Umgebung fehlt jede XML-Faehigkeit (nicht einmal DOMDocument existiert), SAML liesse sich dort weder bauen noch testen. Dazu eigenes SP-Zertifikat samt Erneuerung, eine Tabelle gegen Wiedereinspielung, eine umfangreiche Pruefliste fuer eingehende Assertions und der automatische Zertifikatswechsel von ADFS als haeufigste Stoerungsursache. Empfehlung bleibt OIDC ueber Entra; SAML waere Stufe 3 nach Magic-Link und OIDC, mit der Erweiterungspruefung auf dem Zielhost als erster Aufgabe. Co-Authored-By: Claude Opus 4.8 --- docs/m9-anmeldung-magic-link-und-sso.md | 169 ++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/docs/m9-anmeldung-magic-link-und-sso.md b/docs/m9-anmeldung-magic-link-und-sso.md index 7b8298e..59b8d76 100644 --- a/docs/m9-anmeldung-magic-link-und-sso.md +++ b/docs/m9-anmeldung-magic-link-und-sso.md @@ -215,6 +215,175 @@ die Admin-Oberfläche summieren sich. 4. Admin-Oberfläche inklusive Testanmeldung. 5. `enforce_sso` scharfschalten, Notzugangs-Regeln und Protokollierung. +--- + +## Alternative: SAML 2.0 direkt gegen ADFS + +Nur relevant, wenn ein Kunde die Anmeldung **direkt** am ADFS will statt über +Entra ID. Hier steht, was das zusätzlich kostet — als Entscheidungsgrundlage, +nicht als beschlossener Weg. + +### Voraussetzungen (zuerst klären) + +SAML heißt XML, und XML heißt PHP-Erweiterungen: + +```text +ext-dom Pflicht +ext-simplexml Pflicht +ext-mbstring Pflicht +ext-openssl Pflicht (vorhanden) +ext-zlib für Redirect-Binding (vorhanden) +Composer für die Bibliothek +``` + +**In der aktuellen Dev-Umgebung ist davon nichts vorhanden** — `DOMDocument` +existiert nicht einmal. SAML lässt sich hier weder entwickeln noch testen. Vor +allem anderen muss deshalb geklärt werden: + +1. Welche Erweiterungen bringt der Netcup-Webspace mit (`php -m` dort)? +2. Wie sieht eine Entwicklungsumgebung aus, in der man das überhaupt bauen kann? + +Ist Punkt 1 negativ, endet der SAML-Weg hier. + +### Bibliothek + +`onelogin/php-saml` (v4) ist der De-facto-Standard und die realistische Wahl. +SimpleSAMLphp ist ein vollständiges Föderations-Framework und für einen +einzelnen Service Provider deutlich überdimensioniert. + +**Selbst implementieren ist ausgeschlossen.** XML-Signaturprüfung ist der +klassische Ort für *XML Signature Wrapping*: Der Angreifer hängt eine zweite, +manipulierte Assertion so ins Dokument, dass die Signaturprüfung das echte +Element prüft, die Anwendung aber das gefälschte liest. Diese Klasse von +Fehlern hat über Jahre reihenweise SAML-Implementierungen getroffen. + +### Datenmodell + +`tenant_sso_providers` um SAML-Felder erweitern (oder eigene Tabelle): + +```text +protocol VARCHAR(10) 'oidc' | 'saml' +idp_entity_id VARCHAR(500) +idp_sso_url VARCHAR(500) +idp_slo_url VARCHAR(500) optional +idp_x509_certs TEXT mehrere! siehe Zertifikatswechsel +idp_metadata_url VARCHAR(500) für automatische Aktualisierung +sp_entity_id VARCHAR(500) +attr_email VARCHAR(200) welches Attribut die E-Mail trägt +``` + +Zusätzlich eine Tabelle gegen Wiedereinspielung: + +```text +saml_seen_assertions assertion_id (UNIQUE), tenant_id, expires_at +``` + +Ohne diese Tabelle kann eine einmal abgefangene Assertion innerhalb ihres +Gültigkeitsfensters mehrfach eingelöst werden. + +### Endpunkte + +Alle pfadbasiert auf `APP_HOST`, **keine** Subdomain je Mandant: + +```text +/saml/metadata.php?tenant= SP-Metadaten (XML) für den Kunden +/saml/acs.php?tenant= Assertion Consumer Service (HTTP-POST) +/saml/sls.php?tenant= Single Logout (optional) +``` + +### SP-Zertifikat + +Der Service Provider braucht ein eigenes Schlüsselpaar (selbstsigniert +genügt), dessen öffentlicher Teil in den Metadaten steht: + +- Privater Schlüssel **außerhalb** des Webroots, Rechte 0600. +- Laufzeit und Erneuerung einplanen — läuft es ab, bricht die Anmeldung. +- Bei Erneuerung müssen die Kunden die Metadaten neu importieren, sofern sie + sie nicht per URL automatisch beziehen. + +### Prüfliste für eingehende Assertions + +Diese Punkte muss die Umsetzung nachweislich abdecken: + +- Die **Assertion** selbst muss signiert sein, nicht nur die Response. +- `Issuer` stimmt mit `idp_entity_id` des Mandanten überein. +- `Audience` ist unsere `sp_entity_id`. +- `Destination`/`Recipient` entsprechen unserer ACS-URL. +- `NotBefore` / `NotOnOrAfter` gültig, mit definierter Toleranz. +- `InResponseTo` passt zu einer von uns gestellten Anfrage. +- `assertion_id` wurde noch nicht verwendet (Wiedereinspielung). +- Unsignierte Assertions und SHA-1 werden **abgelehnt**. + +Zur Zeittoleranz: SAML reagiert empfindlich auf Uhrabweichungen. Die +Angleichung von PHP und MySQL (siehe Commit `064c872`) ist dafür Voraussetzung; +zusätzlich sollte die Serverzeit per NTP laufen. + +### ADFS-Besonderheiten + +- **Automatischer Zertifikatswechsel**: ADFS erneuert sein Token-Signing- + Zertifikat standardmäßig selbsttätig (jährlich, Ankündigung 30 Tage vorher). + Wer nur ein Zertifikat fest hinterlegt, steht an diesem Tag still. Deshalb + entweder **mehrere** Zertifikate parallel akzeptieren oder die + IdP-Metadaten regelmäßig per Cron neu einlesen. Das ist die mit Abstand + häufigste Störungsursache bei ADFS-Anbindungen. +- **Claim Rules** muss der Kunde bei sich anlegen: E-Mail bzw. UPN als NameID + oder als Attribut. Ohne passende Regel kommt eine Assertion ohne E-Mail an. +- ADFS erwartet SHA-256; SHA-1 ist abgekündigt. +- Der Kunde importiert unsere Metadaten als **Relying Party Trust** (per URL + oder Datei). + +### Mandantenfähigkeit + +Ein Service Provider, viele IdPs. Der Mandant ergibt sich aus dem +aufgerufenen ACS-Pfad **und** muss zusätzlich gegen den `Issuer` der Assertion +geprüft werden. Stimmen beide nicht überein, wird abgelehnt — sonst könnte +eine gültige Assertion aus Mandant A an der ACS-URL von Mandant B eingereicht +werden. + +Es gelten unverändert die Regeln aus dem OIDC-Teil: kein automatisches +Anlegen von Konten, `saas_session_login()` direkt mit festem Mandanten, nie +über die Mandantenauswahl. + +### Oberfläche für den Mandanten-Administrator + +1. SP-Metadaten zum Herunterladen bzw. als URL zum Kopieren. +2. IdP-Metadaten hinterlegen: als URL (bevorzugt, ermöglicht automatische + Aktualisierung) oder als hochgeladene XML-Datei. +3. Beim Einlesen fremder XML-Dateien externe Entitäten und Netzwerkzugriffe + unterbinden (XXE); unter PHP 8 ist das Standard, muss aber abgesichert + bleiben. +4. Testanmeldung, bevor `enforce_sso` aktiviert werden darf. + +### Testbarkeit + +Ohne echtes ADFS zum Ausprobieren wird das nichts. Möglichkeiten, absteigend +nach Aussagekraft: ein ADFS-Testsystem des Kunden, ein lokales SimpleSAMLphp +als Test-IdP, oder Entra ID als SAML-IdP konfiguriert (der Kunde hat es +ohnehin) — Letzteres testet den Weg allerdings nicht gegen ADFS selbst. + +### Aufwand im Vergleich + +| | OIDC (Entra) | SAML (ADFS) | +|---|---|---| +| Zusätzliche PHP-Erweiterungen | keine | dom, simplexml, mbstring | +| Composer nötig | nein | ja | +| Fremdbibliothek | optional | zwingend | +| Eigene Zertifikate | nein | ja, inkl. Erneuerung | +| Zertifikatswechsel der Gegenseite | entfällt | jährlich, bricht sonst | +| Wiedereinspielungsschutz | über `nonce` | eigene Tabelle nötig | +| Prüfschritte am Token | überschaubar | umfangreiche Prüfliste | +| In der Dev-Umgebung baubar | ja | **nein** | + +### Empfehlung + +Weiterhin OIDC über Entra ID. SAML lohnt sich nur, wenn ein Kunde +ausdrücklich kein Entra für diese Anwendung zulässt — und selbst dann wäre der +Umweg über einen Broker (Keycloak/Authentik übersetzt SAML nach OIDC) meist +günstiger als eine eigene SAML-Implementierung samt Zertifikatspflege. + +Falls SAML doch kommt: **nach** Stufe 1 und 2, als eigenständige Stufe 3, und +mit der Erweiterungsprüfung auf dem Zielhost als erster Aufgabe. + ## Offene Punkte - **Composer**: Für OIDC nicht zwingend nötig (`openssl` + `json` +