Skip to content

Check credentials and allowed methods in the XMLHttpRequest preflight - #1187

Merged
rbri merged 2 commits into
HtmlUnit:masterfrom
Vidya-m78:xhr-preflight-credentials-methods
Nov 7, 2026
Merged

rbri merged 2 commits into
HtmlUnit:masterfrom
Vidya-m78:xhr-preflight-credentials-methods

Conversation

@Vidya-m78

Copy link
Copy Markdown
Contributor

Repro: a page on http://localhost:8080 that has a cookie for localhost runs x.open('PUT', 'http://localhost:8081/account', false); x.withCredentials = true; x.send(); and the server on 8081 answers the OPTIONS preflight with Access-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 no Access-Control-Allow-Credentials, when Access-Control-Allow-Methods does not list the method (GET, POST for a PUT, or * with credentials), and when Access-Control-Allow-Headers: * is used for a credentialed request with a custom header. fetch() with credentials: 'include' goes through the same code.
Cause: XMLHttpRequest.isPreflightAuthorized() accepts * as origin without looking at the credentials mode, never reads Access-Control-Allow-Credentials or Access-Control-Allow-Methods, and treats Access-Control-Allow-Headers: * as a wildcard also for credentialed requests. An API that answers every request with Access-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 plus Access-Control-Allow-Credentials: true for credentialed requests), require methods other than GET, HEAD and POST to be listed in Access-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 XMLHttpRequestCORSTest log the request the server received after the preflight. preflight_put_methodNotAllowed, preflight_withCredentials_wildcardHeaders and the three blocked preflight_put_withCredentials_* cases fail without the change (the server gets PUT cookie=sweet); the three allowed cases pass before and after. XMLHttpRequestCORSTest, FetchTest, the other XMLHttpRequest tests and CodeStyleTest pass. I also added a changes.xml entry.

Vidya-m78 and others added 2 commits November 7, 2026 11:53
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.
@rbri
rbri merged commit f0df26c into HtmlUnit:master Nov 7, 2026
5 checks passed
@sonarqubecloud

sonarqubecloud Bot commented Nov 7, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants