Onion host: FORCE_SSL_ADMIN makes wp-login.php / wp-admin unreachable #20

Closed
opened 2026-10-16 13:08:36 -07:00 by eric · 0 comments
Owner

Type: availability. Follow-up to #16.

What happens

With FORCE_SSL_ADMIN on, a plain-http request to wp-login.php on a configured onion host gets core's https-upgrade redirect (wp-login.php:15, auth_redirect()). The URL rewriter always emits onion URLs as http, so the Location comes back as http://<onion>/wp-login.php and the browser loops. Without the plugin, core would send the visitor to https://<onion>/, which normally has no TLS listener, so onion admin is unusable either way.

auth_redirect() also reads SECURE_AUTH_COOKIE whenever force_ssl_admin() is true. A plain-http onion login never sets that cookie, so stopping only the redirect would still bounce wp-admin back to the login page.

Proposed fix

When the request resolves to an onion host, call force_ssl_admin( false ) while the plugin boots. Core's wp_ssl_constants() has already run by then (it runs before regular plugins load), so this overrides FORCE_SSL_ADMIN for that request only. This switches off the wp-login upgrade, the auth_redirect() upgrade and secure-cookie check, and https admin/login URL schemes together. Also filter secure_signon_cookie to false on onion requests so the legacy per-user use_ssl option can't issue a Secure-only auth cookie over http.

Clearnet hosts are unaffected. Onion services are end-to-end encrypted by Tor, so FORCE_SSL_ADMIN's purpose (no plaintext credentials or cookies) still holds. A network attacker who could make a victim's browser send Host: <onion> over clearnet could equally serve their own fake login page, so this adds no attack surface. Auth cookies stay host-only (or COOKIE_DOMAIN-scoped), so clearnet cookies are never sent to the onion host.

**Type:** availability. Follow-up to #16. ## What happens With `FORCE_SSL_ADMIN` on, a plain-http request to `wp-login.php` on a configured onion host gets core's https-upgrade redirect (`wp-login.php:15`, `auth_redirect()`). The URL rewriter always emits onion URLs as `http`, so the `Location` comes back as `http://<onion>/wp-login.php` and the browser loops. Without the plugin, core would send the visitor to `https://<onion>/`, which normally has no TLS listener, so onion admin is unusable either way. `auth_redirect()` also reads `SECURE_AUTH_COOKIE` whenever `force_ssl_admin()` is true. A plain-http onion login never sets that cookie, so stopping only the redirect would still bounce wp-admin back to the login page. ## Proposed fix When the request resolves to an onion host, call `force_ssl_admin( false )` while the plugin boots. Core's `wp_ssl_constants()` has already run by then (it runs before regular plugins load), so this overrides `FORCE_SSL_ADMIN` for that request only. This switches off the wp-login upgrade, the `auth_redirect()` upgrade and secure-cookie check, and https `admin`/`login` URL schemes together. Also filter `secure_signon_cookie` to `false` on onion requests so the legacy per-user `use_ssl` option can't issue a Secure-only auth cookie over http. Clearnet hosts are unaffected. Onion services are end-to-end encrypted by Tor, so `FORCE_SSL_ADMIN`'s purpose (no plaintext credentials or cookies) still holds. A network attacker who could make a victim's browser send `Host: <onion>` over clearnet could equally serve their own fake login page, so this adds no attack surface. Auth cookies stay host-only (or `COOKIE_DOMAIN`-scoped), so clearnet cookies are never sent to the onion host.
eric closed this issue 2026-10-16 13:10:03 -07:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
eric/multi-domain-redux#20
No description provided.