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 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 10:44:25 +02:00
co-authored by Claude Opus 4.8
parent c6a03c1a38
commit fb4dcf96fd
+169
View File
@@ -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=<slug> SP-Metadaten (XML) für den Kunden
/saml/acs.php?tenant=<slug> Assertion Consumer Service (HTTP-POST)
/saml/sls.php?tenant=<slug> 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` +