Fix the findings of a full second security audit
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
Four-part audit (OIDC/JWT/crypto, user sync, admin UI, login bypasses) with dynamic PoCs against a real WordPress install; every fix is covered by a regression test. Report: docs/security-audit.md, section 6. Critical/High - Multisite: settings, AJAX actions and certificate download require manage_network_options (site admins could sign in as super admin). - Privileged accounts are only linked (sync and first sign-in) via a matching UPN of a member account, never via the settable mail attribute; the sync never changes their e-mail address; e-mail change notifications stay on. - Button-only mode exempts by credential (application passwords, WP-CLI) instead of request context, closing bypasses through xmlrpc.php and REST login handlers; API requests never receive login cookies. - Multi-tenant mode refuses guest/external identities. Medium/Low - Same message for right and wrong passwords; button-only no longer switches off when the connection breaks; server-side fallback cookie expiry; correct fallback key beats IP lockouts; right-most proxy hop; higher start limit; one object ID per account. - Deactivation sets a random password, revokes application passwords and removes the role (restored on reactivation); disabled people are deactivated even when their mail vanished; duplicate bindings handled. - Sync: abort on empty directory answer, no deprovisioning right after a tenant change, atomic run lock, strict photo path validation. - Certificates: key bundles refused, clean re-exported certificate. - Array-safe sanitising, encoded redirect_to, per-action nonces, escaped role lists, no Graph sleeps during sign-in, warnings for public groups, multi-tenant group rules and missing salts, uninstall clears the token. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
791f43a80b
commit
850f0dcd54
18 changed files with 1908 additions and 1270 deletions
|
|
@ -34,7 +34,8 @@ The plugin is deliberately small and strict:
|
|||
* Optional **tenant pinning**: when a tenant GUID is configured, tokens from any other tenant are rejected.
|
||||
* **Account binding**: on first sign-in the immutable Microsoft object ID is stored with the user; later sign-ins with the same e-mail but a different Microsoft identity are refused.
|
||||
* Optional **e-mail domain allow-list** and **group allow-list** (fails closed when membership cannot be verified).
|
||||
* **Button-only mode** blocks password sign-in server-side, not just visually; the fallback key is rate limited and never stored in a cookie.
|
||||
* **Button-only mode** blocks password sign-in server-side, not just visually – everywhere, XML-RPC and other plugins' login handlers included (application passwords and WP-CLI keep working, API requests never get a login cookie). Right and wrong passwords get the same answer. The fallback cookie carries its issue time and expires on the server.
|
||||
* Administrator accounts are only linked (by sign-in or sync) through a matching user principal name of a member account, never through the freely settable e-mail attribute; one Microsoft identity can only be bound to one WordPress account.
|
||||
* **Certificate authentication** (RFC 7523 client assertion) as an alternative to a client secret: generate a 3072-bit key pair in the settings, upload only the public certificate to Entra ID. Nothing secret is ever transmitted.
|
||||
* The **client secret / private key is encrypted at rest** (AES-256-GCM, key derived from your WordPress salts) and never displayed again.
|
||||
* In multi-tenant mode the unverified `email` claim is ignored; matching uses the user principal name (verified domain) only.
|
||||
|
|
@ -132,11 +133,11 @@ Yes. Forms rendered with `wp_login_form()` get the button automatically. For pag
|
|||
|
||||
= Does it support multisite? =
|
||||
|
||||
Yes. Settings are per site; a user must be a member of the site (or a super admin) to sign in.
|
||||
Yes. Settings are per site but can only be changed by super admins, because they decide which Microsoft identity may sign in as which network-wide WordPress user. A user must be a member of the site (or a super admin) to sign in.
|
||||
|
||||
= What happens on uninstall? =
|
||||
|
||||
The settings, cached data, the sync report and schedule, stored profile photos and the per-user plugin data (Microsoft object ID, deactivation status) are removed. Imported accounts and copied profile fields (`m365_*` user meta) are kept. Deactivated accounts become active again, so delete them first if they must stay locked.
|
||||
The settings, cached data, the sync report and schedule, stored profile photos and the per-user plugin data (Microsoft object ID, deactivation status) are removed. Imported accounts and copied profile fields (`m365_*` user meta) are kept. Deactivated accounts stay without a role, with a random password and without application passwords.
|
||||
|
||||
== Screenshots ==
|
||||
|
||||
|
|
@ -156,6 +157,7 @@ The settings, cached data, the sync report and schedule, stored profile photos a
|
|||
* New: `wp m365-login sync [--dry-run]` WP-CLI command and scheduled sync via WP-Cron.
|
||||
* New: excluded Entra groups – their members can never sign in with Microsoft.
|
||||
* Fix: failed Microsoft sign-ins (e.g. expired secret, group not allowed) ended in a PHP fatal error instead of the error message.
|
||||
* Security: fixes from a full security audit – see docs/security-audit.md (button-only bypasses via XML-RPC/REST, administrator linking, multisite settings restricted to super admins, deactivation hardening, and more).
|
||||
* Fix: generating or removing the certificate in the settings did not keep the change and broke a stored client secret.
|
||||
|
||||
= 1.0.0 =
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue