Add Microsoft 365 user sync with roles, profile fields and deprovisioning
Some checks are pending
CI / PHP lint (7.4) (pull_request) Waiting to run
CI / PHP lint (8.0) (pull_request) Waiting to run
CI / PHP lint (8.1) (pull_request) Waiting to run
CI / PHP lint (8.2) (pull_request) Waiting to run
CI / PHP lint (8.3) (pull_request) Waiting to run
CI / PHP lint (8.4) (pull_request) Waiting to run
CI / WordPress Coding Standards (pull_request) Waiting to run
CI / WordPress.org Plugin Check (pull_request) Waiting to run
Some checks are pending
CI / PHP lint (7.4) (pull_request) Waiting to run
CI / PHP lint (8.0) (pull_request) Waiting to run
CI / PHP lint (8.1) (pull_request) Waiting to run
CI / PHP lint (8.2) (pull_request) Waiting to run
CI / PHP lint (8.3) (pull_request) Waiting to run
CI / PHP lint (8.4) (pull_request) Waiting to run
CI / WordPress Coding Standards (pull_request) Waiting to run
CI / WordPress.org Plugin Check (pull_request) Waiting to run
New "User sync" tab that imports Microsoft 365 / Entra ID users as WordPress accounts and keeps them up to date: - Scope: whole tenant or the (nested) members of selected groups, guests optional, e-mail domain allow-list respected. Existing accounts are linked by e-mail address. - Roles: selectable default role plus a group -> role mapping (in addition to or instead of the default role, first match wins). Roles of pre-existing accounts are only managed on request. - Profile: selectable Graph attributes (names, job title, department, phones, address, language, ...) and the profile photo as avatar. - Deprovisioning: accounts disabled or deleted in Microsoft 365 (or removed from the sync groups) are deactivated or deleted; accounts deactivated by the sync are reactivated automatically. Deactivated accounts lose every sign-in path and all sessions. - Safeguards: dry run, safety stop above 20 % (min. 5) deprovisioning, abort on any Graph error, "deleted" only on a 404 for the object ID, protected pre-existing administrators and own account, content reassignment required for deletion, run lock. - Runs manually, via WP-Cron or `wp m365-login sync [--dry-run]`. - Users screen column with deactivate/reactivate row actions and a read-only Microsoft 365 section on the profile screen. The Graph client gains paging, retry on throttling and user, group member and photo endpoints. The group picker is now reusable. Version 1.1.0, German translations (du/Sie), docs and audit addendum. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1708bae91a
commit
4edf20bc45
20 changed files with 5532 additions and 982 deletions
|
|
@ -1,6 +1,6 @@
|
|||
# Security-Audit: M365 Login 1.0.0
|
||||
# Security-Audit: M365 Login 1.1.0
|
||||
|
||||
**Stand:** 22.09.2026 · **Umfang:** gesamter Plugin-Code (PHP, JS, CSS), Konfiguration, Deployment-Hinweise ·
|
||||
**Stand:** 22.09.2026, Nachtrag Benutzer-Sync 23.09.2026 · **Umfang:** gesamter Plugin-Code (PHP, JS, CSS), Konfiguration, Deployment-Hinweise ·
|
||||
**Methode:** manuelle Code-Review gegen OAuth 2.0 / OpenID Connect Best Current Practice (RFC 6749, RFC 7636 PKCE,
|
||||
RFC 7523 Client Assertions, OAuth 2.0 Security BCP), OWASP ASVS 4.0 (V2 Authentication, V3 Session, V5 Validation,
|
||||
V6 Cryptography), WordPress Plugin Handbook „Security“ sowie isolierte Tests der sicherheitskritischen Klassen.
|
||||
|
|
@ -8,6 +8,8 @@ V6 Cryptography), WordPress Plugin Handbook „Security“ sowie isolierte Tests
|
|||
> Der Audit wurde ohne laufende WordPress-Instanz durchgeführt. Alle Aussagen zum Laufzeitverhalten beruhen auf
|
||||
> Code-Lesung und den isolierten Tests (JWT-Verifikation, Verschlüsselung, Zertifikate, Eingabeverarbeitung,
|
||||
> Nur-Button-Sperre). Ein Penetrationstest gegen eine echte Installation steht aus und wird empfohlen.
|
||||
> Der Benutzer-Sync (N-7) wurde zusätzlich in einer echten WordPress-Installation (7.1, SQLite) gegen eine simulierte
|
||||
> Graph-API getestet (Import, Paging, Rollen, Profilfelder, Fotos, Deaktivierung, Löschen, Sicherheitsstopp, Graph-Fehler).
|
||||
|
||||
## 1. Zusammenfassung
|
||||
|
||||
|
|
@ -112,6 +114,32 @@ Alle Requests laufen über die WordPress-HTTP-API mit Zertifikatsprüfung. Setzt
|
|||
fälschen. Das Plugin erzwingt `sslverify => true` für seine eigenen Requests nicht explizit, weil WordPress-Konventionen
|
||||
den Site-Betreiber entscheiden lassen. Empfehlung: `https_ssl_verify` nie global deaktivieren.
|
||||
|
||||
### N-7 · Benutzer-Sync (1.1.0) — **neu, geprüft**
|
||||
|
||||
Der Sync legt Konten an, ändert Rollen und deaktiviert bzw. löscht Konten. Geprüft und abgesichert:
|
||||
|
||||
- **Auslösung:** nur durch Administratoren (`manage_options` + `create_users`, AJAX-Nonce), per WP-Cron nach expliziter
|
||||
Aktivierung oder per WP-CLI. Der Login selbst legt weiterhin nie Konten an.
|
||||
- **Vertrauensgrenze:** Wie beim Login gilt der gepinnte Tenant als vertrauenswürdig (Sync verlangt eine Tenant-GUID).
|
||||
Bestehende Konten werden über die E-Mail-Adresse verknüpft; ein Konto mit abweichender gespeicherter Objekt-ID wird
|
||||
übersprungen, eine E-Mail-Änderung auf eine bereits vergebene Adresse abgelehnt.
|
||||
- **Fehlkonfiguration / Teilausfälle:** Jede fehlgeschlagene Graph-Anfrage bricht den Lauf vor jeder Deaktivierung ab.
|
||||
„Gelöscht“ nur bei HTTP 404 für die konkrete Objekt-ID. Mehr als 20 % (mind. 5) Deaktivierungen/Löschungen pro Lauf →
|
||||
Sicherheitsstopp ohne Änderungen. Testlauf ohne Schreibzugriffe. Sperre gegen Parallelläufe.
|
||||
- **Rechteausweitung/-entzug:** Rollen werden nur bei importierten Konten (oder auf ausdrücklichen Wunsch) verwaltet.
|
||||
Bestehende Administratoren, Super-Admins und das eigene Konto werden nie umgestuft, deaktiviert oder gelöscht.
|
||||
Rollen-Slugs werden beim Speichern gegen existierende Rollen geprüft.
|
||||
- **Deaktivierung:** blockiert Passwort- und Anwendungspasswort-Logins (`authenticate`, Priorität 100), bestehende Sessions
|
||||
(`determine_current_user`, alle Session-Tokens werden gelöscht) und den Microsoft-Login. Löschen nur mit Übernahme der
|
||||
Inhalte durch einen gültigen anderen Benutzer, sonst Deaktivierung.
|
||||
- **Profilbilder:** Größenlimit 2 MB, Typprüfung per `getimagesizefromstring` (nur JPEG/PNG/GIF), Ablage über `wp_upload_bits`
|
||||
in einem eigenen Unterordner, Dateiname aus gesalzenem Hash (keine Objekt-ID in der URL). Die Bilder sind – wie Gravatare – öffentlich.
|
||||
- **Graph-Aufrufe:** nur `https://graph.microsoft.com/v1.0/`; Paging-Links werden auf diesen Präfix geprüft, IDs sind GUIDs.
|
||||
- **Ausgabe:** Protokoll und Profilfelder werden escaped ausgegeben; Benutzer-Zeilenaktionen mit Nonce und `edit_user`.
|
||||
|
||||
Hinweis für den Betrieb: Personenbezogene Daten (Telefon, Adresse, Foto) nur synchronisieren, wenn sie auf der Website
|
||||
gebraucht werden; die Auswahl ist bewusst Opt-in (Standard: nur Namen).
|
||||
|
||||
## 4. Geprüfte Kontrollen (ohne Befund)
|
||||
|
||||
| Bereich | Kontrolle | Ergebnis |
|
||||
|
|
@ -120,7 +148,7 @@ den Site-Betreiber entscheiden lassen. Empfehlung: `https_ssl_verify` nie global
|
|||
| State-Bindung | HMAC-Schlüssel in DB, Klartext nur in URL; HttpOnly/SameSite=Lax/Secure-Cookie mit separatem Token, Hash im Datensatz; einmalige Einlösung (Delete vor Prüfung); TTL 10 Min. | ✔ Login-CSRF und Replay ausgeschlossen |
|
||||
| Token-Austausch | Server-zu-Server, Secret/Assertion nie im Browser; `redirect_uri` fest aus `home_url()` | ✔ |
|
||||
| ID-Token | Nur `RS256`; `alg=none`/HMAC abgelehnt; `kid` Pflicht; JWKS über HTTPS, Cache 12 h, Refresh bei unbekanntem `kid`; `iss` gegen `tid` gebildet, `aud`, `tid` (Pinning), `exp`/`nbf`/`iat` mit 120 s Toleranz, `nonce` mit `hash_equals` | ✔ 11 Negativtests |
|
||||
| Benutzerzuordnung | Kein Provisioning; E-Mail lowercase + `is_email`; Domain-Allowlist; Gruppen-Check fail closed; `oid`-Bindung; Multisite-Mitgliedschaft | ✔ |
|
||||
| Benutzerzuordnung | Login ohne Provisioning (Sync separat, siehe N-7); E-Mail lowercase + `is_email`; Domain-Allowlist; Gruppen-Check fail closed; `oid`-Bindung; Multisite-Mitgliedschaft | ✔ |
|
||||
| Session | `wp_set_auth_cookie` nach Erfolg (neues Session-Token, keine Fixation); `login_redirect`-Filter; `wp_safe_redirect` überall | ✔ |
|
||||
| Offene Redirects | `redirect_to` → `wp_validate_redirect`; Custom-Login-URL → `wp_validate_redirect` beim Speichern und beim Lesen | ✔ |
|
||||
| SSRF | Tenant nur GUID oder Whitelist-Wort, `rawurlencode`; Graph-Pfade mit `rawurlencode`; keine benutzerkontrollierten Hosts | ✔ |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue