Application Passwords: verify Basic Auth with the server before blocking. - #13597
Open
Aayushkalikote wants to merge 1 commit into
Open
Aayushkalikote wants to merge 1 commit into
Aayushkalikote wants to merge 1 commit into
Conversation
…ing. The presence of PHP_AUTH_USER or PHP_AUTH_PW is not proof that the server enforces HTTP Basic Auth. Under the CGI/FastCGI SAPI, PHP populates those keys from any Authorization header the client sends, and browsers keep replaying cached credentials for the rest of the session, so the Application Passwords warning persists after Basic Auth is disabled. Confirm with the server via an unauthenticated loopback request when credentials are present, looking for a 401 with a Basic challenge. The check is skipped entirely when no credentials are present, the result is cached in a short-lived transient, and a failed loopback request preserves the previous behaviour. Adds test coverage for wp_is_site_protected_by_basic_auth(), which had none. Props ethicaladitya, khokansardar, smeunus. Fixes #66000. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Trac ticket: https://core.trac.wordpress.org/ticket/66000
The problem
wp_is_site_protected_by_basic_auth()treats the presence ofPHP_AUTH_USER/PHP_AUTH_PWas proof that the server enforces HTTP Basic Auth:Under the CGI/FastCGI SAPI, PHP populates those keys from any
Authorization: Basicheader the client chooses to send — no server-side Basic Auth needs to be configured for them to appear. Browsers keep replaying cached credentials to an origin for the rest of the session, so after Basic Auth is switched off the affected browser keeps seeing:while a fresh browser or private window works normally. That notice hides the form in
wp-admin/user-edit.phpand hard-failswp-admin/authorize-application.phpwith a 501.As noted on the ticket, the two cases are indistinguishable from within the request —
AUTH_TYPEandREMOTE_USERare unset for both a genuinely protected site and a stale credential, so neither can discriminate.The change
The distinction does exist on the wire: a server that enforces Basic Auth must answer an unauthenticated request with
401and aWWW-Authenticate: Basicchallenge, and a browser replaying cached credentials cannot produce that. This is the loopback check [50006] named as the intended robust evaluation when the function was introduced for #52066.wp_is_site_protected_by_basic_auth()keeps its existing credential check. When credentials are present, it now confirms with the server through a new private helper,wp_is_basic_auth_enforced_by_server(): an unauthenticatedwp_remote_head()tohome_url( '/' )looking for a401plus aBasicchallenge.wp_basic_auth_enforcedtransient for 15 minutes. The lifetime is deliberately short: the server's configuration can change at any time, and a long-lived cache reproduces this ticket's own symptom until it expires.true, exactly as before this check existed, and the failure is not cached so the next request can retry. Sites behind Basic Auth with loopback requests blocked see no change, so the conflict #52066 was opened to prevent stays prevented.wp_is_site_protected_by_basic_authstill short-circuits everything, so the documented__return_falseworkaround keeps working.Tests
wp_is_site_protected_by_basic_auth()had no PHPUnit coverage at all.tests/phpunit/tests/load/wpIsSiteProtectedByBasicAuth.phpadds it, mocking the loopback request throughpre_http_request:200response →false(this ticket)401with a non-Basic challenge →false401with a Basic challenge →truetrue, and not cachedwp_is_site_protected_by_basic_authfilter overriding in both directionsTesting instructions
Manual, reproducing the original report:
Users → Profile → Application Passwords. The warning appears — correct.wp_basic_auth_enforcedtransient, and reload. The warning returns.Verified on trunk, PHP 8.5 (fpm-fcgi), nginx 1.31.5:
falsefalseAuthorization: Basicfor a credential the server has never seen, no Basic Auth configuredtruefalseauth_basicenabled, valid credentialstruetrueThe middle row is this ticket. The last row is the #52066 behaviour, unchanged.
Open questions
Props ethicaladitya, khokansardar, smeunus.