Why can't I use a wildcard * on Access-Control-Allow-Headers when Allow-Credentials is set to true? - firefox

I have a website where the React frontend resides in (let's say) app.mydomain.com and the api on api.mydomain.com.
The user submits a login request to the API and, upon logging in successfully, receives a nice cookie to be used later. The front-end talks only and directly to the API so the domain on the cookie is simply set to api.mydomain.com.
I use Axios to perform the requests with the withCredentials flag set to true, in order to receive the cookie.
The headers on the server to allow CORS are as follows:
Access-Control-Allow-Origin: http://app.mydomain.com
Access-Control-Allow-Methods: GET,POST,DELETE,PUT,OPTIONS
Access-Control-Allow-Headers: *
Access-Control-Allow-Credentials: true
In this situation this is the response from Firefox:
But, as soon as the Access-Control-Allow-Headers value is set more specifically, say to
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, everything works.
Mozilla says they do not allow "wildcarding" the Origin value, not the Headers one, same in this page, where nothing is mentioned.
Why is Firefox behaving like this and why is it not mentioned anywhere that I can find?

In a preflight response that includes Access-Control-Allow-Credentials: true, an asterisk used as the value of header Access-Control-Allow-Headers is interpreted literally, not as a wildcard (i.e. "all headers allowed").
It's true that the main MDN page about CORS doesn't explicitly state this rule. However, the more specific MDN page about the Access-Control-Allow-Headers header does so explicitly:
The value "*" only counts as a special wildcard value for requests without credentials (requests without HTTP cookies or HTTP authentication information). In requests with credentials, it is treated as the literal header name "*" without special semantics. Note that the Authorization header can't be wildcarded and always needs to be listed explicitly.
Edit (2023): As sideshowbarker pointed out in his comments, the MDN Web Docs page about CORS has since been updated and now states the following:
When responding to a credentialed request: [...]
The server must not specify the "*" wildcard for the Access-Control-Allow-Headers response-header value, but must instead specify an explicit list of header names; for example, Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Here are clarifying quotes from the more authoritative Fetch standard:
For Access-Control-Expose-Headers, Access-Control-Allow-Methods, and Access-Control-Allow-Headers response headers, the value * counts as a wildcard for requests without credentials.
Access-Control-Expose-Headers, Access-Control-Allow-Methods, and Access-Control-Allow-Headers response headers can only use * as value when request’s credentials mode is not "include".
(my emphasis)
The relevant normative requirement for browsers that the spec states is in the main fetch algorithm at https://fetch.spec.whatwg.org/#main-fetch, in step 13, substep 2:
If request's credentials mode is not
"include" and headerNames contains *, then set response’s CORS-exposed header-name list to all unique header names in response’s header list.
In other words, the browser behavior that spec statement requires for * is:
if the credentials mode is not "include", the browser is required to examine the actual list of response headers in the response, and allow all of them
if the credentials mode is "include", the browser is required to only allow any response header with the literal name "*"
At the end of that step, there’s actually a note saying pretty much the same thing:
One of the headerNames can still be * at this point, but will only match a header whose name is *.

Related

How to pass xsft token through cookie

The recorded request only have xsrf token in response header and while executing the script the xsrf token not come from database
Extract it from the response header using i.e. Regular Expression Extractor, just make sure to set "Field to check" to Response Headers
textual representation of the regular expression just in case:
YOUR_HEADER_NAME_HERE\s*:\s*(.*)
Once done you can add it to the next request using HTTP Cookie Manager

Spring MVC response Headers: ETag has double quotes for GET request but not for PUT request

We upgraded our Spring MVC from 4.0 to 4.3 in our service. It caused ETag format change in the response headers for "GET" method. Clients making "GET" calls will get ETags with double quotes in the response headers. Previously ETag in the response headers had no double quotes for "GET" method.
For example:
Now: etag →"TVMzTWFpbmxpbmVEZXZvLTI5ODIxMQ"
Previously: etag →TVMzTWFpbmxpbmVEZXZvLTI5ODIxMQ
The response of "PUT" request does not have double quotes around ETag in Headers, just like before.
Anyone has any ideas why?
Prior to Spring 4.2.x, there was no management for the ETag header. It has been introduced since then in the HttpEntityMethodProcessor. The class has evolved during time and the management of the ETag header respect the RFC (or is close enough).
As you can see in this commit, Spring team fixed an issue with its management:
Fix missing ETag/LastModified headers in responses
Prior to this commit, the HttpEntityMethodProcessor would avoid
writing ETag/Last-Modified response headers before calling
ServletWebRequest to process conditional requests. This was done to
avoid duplicate response header values due to headers being already
written to the underlying servlet response.
This is still necessary for GET/HEAD requests, since this is properly
handled by ServletWebRequest for those cases. But
HttpEntityMethodProcessor should not make that decision for
PUT/PATCH/POST responses since developers are adding response headers on
purpose and should be in control of the situation — whereas
ServletWebRequest does not write those headers in those cases.
The relevant part of modified code is here.
So basically, when you are manually adding an ETag header, in case of a 200 status of a GET or HEAD method, the framework removes it, then recreates it. That is why with a PUT, there are no double quotes.
In HttpEntityMethodProcessor:
if (inputMessage.getMethod() == HttpMethod.GET || inputMessage.getMethod() == HttpMethod.HEAD) {
responseHeaders.remove(HttpHeaders.ETAG);
responseHeaders.remove(HttpHeaders.LAST_MODIFIED);
}
Then in ServletWebRequest:
private String padEtagIfNecessary(String etag) {
if (!StringUtils.hasLength(etag)) {
return etag;
}
if ((etag.startsWith("\"") || etag.startsWith("W/\"")) && etag.endsWith("\"")) {
return etag;
}
return "\"" + etag + "\"";
}
As you can see, this respects the chapter 2.4 of the RFC:
2.4. When to Use Entity-Tags and Last-Modified Dates
In 200 (OK) responses to GET or HEAD, an origin server:
o SHOULD send an entity-tag validator unless it is not feasible to
generate one.
o MAY send a weak entity-tag instead of a strong entity-tag, if
performance considerations support the use of weak entity-tags, or
if it is unfeasible to send a strong entity-tag.
o SHOULD send a Last-Modified value if it is feasible to send one.
In other words, the preferred behavior for an origin server is to
send both a strong entity-tag and a Last-Modified value in successful
responses to a retrieval request.
But I found that it is not backward compatible and breaks what developer could have used prior to these versions, without the possibility to skip/override what they did.
Here is the description of the ETag from the MDN (more clearer).
Hope it helped in comprehension.

okhttp3: cookieJar: how to have saveFromResponse always called with all response cookies

It looks like cookieJar.saveFromResponse(...) will not be called if Cookie.parseAll(...) returns an empty Cookie List: (https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/internal/http/HttpEngine.java#L867)
and Cookie.parseAll(...) excludes response cookies based on domain matching and other logic:(https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/Cookie.java#L302)
Is it possible to bypass the Cookie.parse domain matching logic so that cookieJar.saveFromResponse(...) is called with response cookies that haven't been whittled down by domain?

HttpOpenRequest and Accept header

HttpOpenRequest takes an http accept header in a very weird format. Is it okay to ignore that parameter and add a normal accept header with HttpAddRequestHeaders later ?
hfile = HttpOpenRequest(hLastConnection, 'GET', '/path', NULL, NULL, NULL, INTERNET_FLAG_RELOAD | INTERNET_FLAG_KEEP_CONNECTION, 0);
HttpAddRequestHeaders(hfile,'Accept: foobar', 14, HTTP_ADDREQ_FLAG_REPLACE or HTTP_ADDREQ_FLAG_ADD);
It does work (in WINE), but seems undocumented.
Similarly, how is the situation for content-type and referer?
There is nothing weird about how HttpOpenRequest() accepts the Accept values. It is just an array of null-terminated strings. The Accept header allows for multiple types. HttpOpenRequest() will concatenate the values and format the Accept header for you. With HttpAddRequestHeaders(), you have to do that manually. But whether you use HttpOpenRequest() or HttpAddRequestHeaders() makes no difference, as the Accept header does not affect the client at all, so it does not matter how you get it into the request. It only affects the server when it is preparing the response.
Same with Referer, it does not affect the client, only the server, so add it to the request however you want. Though HttpOpenRequest() has a nice simple parameter for the Referer value so you should use it.
Content-Type, on the other hand, can only be set with HttpAddRequestHeaders(). But it doesn't make sense to specify a Content-Type for a GET request since there is no payload in the request. Content-Type only applies to requests with payloads, like POST and PUT.

Difference between the two Web API headers response.Content.Headers and response.Headers

I find WebAPI separate HTTP response headers into different places, one is in Response.Headers, the other in in Response.Content.Headers. For example, etag is in Response.Headers while lastModified is in the other. What is the reason behind that?
There are a couple of answers to that question. One is because that's the way the HTTP spec defines the headers.
RFC 2616
Content header fields here
https://www.rfc-editor.org/rfc/rfc2616#section-7.1
Request header fields here https://www.rfc-editor.org/rfc/rfc2616#section-5.3
Response header fields here https://www.rfc-editor.org/rfc/rfc2616#section-6.2
The other more practical reason for separating out the content headers is that it is easier write code that processes data into HTTP payloads and sets the related headers, independent of the request/response objects.
Unfortunately, the more recent HTTPbis specification did some reorganization of where they think headers should go and now LastModified and Allow are considered response fields, not content fields.
This means that the headers as defined in System.Net.HttpHeaders will no longer match the spec, which really sucks. It also means that we are probably stuck with LastModified as a HttpContent header and Etag as a response header.
HTTPbis
Content related headers are defined here.
Request headers here.
Response headers here.

Resources