Repository navigation
Check credentials and allowed methods in the XMLHttpRequest preflight - #1187
Merged
rbri merged 2 commits intoNov 7, 2026
Merged
Conversation
isPreflightAuthorized() accepted 'Access-Control-Allow-Origin: *' and did not check Access-Control-Allow-Credentials, so a wildcard preflight was enough to send a request with credentials (cookies) to another origin. Access-Control-Allow-Methods was not checked at all, and 'Access-Control-Allow-Headers: *' was also honoured for requests with credentials. Real browsers stop at the preflight in all these cases and never send the request. Run the preflight response through the CORS check used for the other responses, require methods other than GET, HEAD and POST to be listed in Access-Control-Allow-Methods and support the wildcard for methods and headers only for requests without credentials.
|
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.



Repro: a page on
http://localhost:8080that has a cookie forlocalhostrunsx.open('PUT', 'http://localhost:8081/account', false); x.withCredentials = true; x.send();and the server on 8081 answers the OPTIONS preflight withAccess-Control-Allow-Origin: *. Chrome and Firefox fail the preflight (wildcard origin with credentials) and never send the PUT. HtmlUnit sends the PUT with the cookie; only reading the response fails afterwards. The same happens when the preflight has the exact origin but noAccess-Control-Allow-Credentials, whenAccess-Control-Allow-Methodsdoes not list the method (GET, POSTfor a PUT, or*with credentials), and whenAccess-Control-Allow-Headers: *is used for a credentialed request with a custom header.fetch()withcredentials: 'include'goes through the same code.Cause:
XMLHttpRequest.isPreflightAuthorized()accepts*as origin without looking at the credentials mode, never readsAccess-Control-Allow-CredentialsorAccess-Control-Allow-Methods, and treatsAccess-Control-Allow-Headers: *as a wildcard also for credentialed requests. An API that answers every request withAccess-Control-Allow-Origin: *and relies on the preflight to keep cookie-authenticated PUT/DELETE/JSON requests from other sites away (a common CSRF defence) therefore gets them from any page loaded in the same WebClient.Fix: check the preflight response with the existing
isCorsAllowed()(exact origin plusAccess-Control-Allow-Credentials: truefor credentialed requests), require methods other than GET, HEAD and POST to be listed inAccess-Control-Allow-Methods(case-sensitive, as in the browsers), and accept the*wildcard for methods and headers only without credentials, as the CORS-preflight fetch of the Fetch standard does. Both callers, the initial request and the redirect hops, use this helper.The new tests in
XMLHttpRequestCORSTestlog the request the server received after the preflight.preflight_put_methodNotAllowed,preflight_withCredentials_wildcardHeadersand the three blockedpreflight_put_withCredentials_*cases fail without the change (the server getsPUT cookie=sweet); the three allowed cases pass before and after.XMLHttpRequestCORSTest,FetchTest, the other XMLHttpRequest tests andCodeStyleTestpass. I also added a changes.xml entry.