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:
@@ -215,6 +215,175 @@ die Admin-Oberfläche summieren sich.
|
|||||||
4. Admin-Oberfläche inklusive Testanmeldung.
|
4. Admin-Oberfläche inklusive Testanmeldung.
|
||||||
5. `enforce_sso` scharfschalten, Notzugangs-Regeln und Protokollierung.
|
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
|
## Offene Punkte
|
||||||
|
|
||||||
- **Composer**: Für OIDC nicht zwingend nötig (`openssl` + `json` +
|
- **Composer**: Für OIDC nicht zwingend nötig (`openssl` + `json` +
|
||||||
|
|||||||
Reference in New Issue
Block a user