[whatwg/fetch] Promp for/send TLS client certificates for CORS preflights (#1181)

(This is a follow-up for the StackOverflow question here[1])

## Background

The issue:
- fetch spec currently doesn't allow sending any credentials during preflights[2]
- client certificates are considered credentials

In this context, web servers protected with a `client-certificate: required` (pseudocode) policy won't work in a cross-origin context because preflights will be rejected during the TLS handshake, thus no HTTP will ever happen and the application server won't be able to respond to the preflight.

I.e, the issue is circular (catch-22/chicken-and-egg): TLS lives at a lower lever from HTTP, but CORS is negotiated in HTTP headers. So if setting up TLS itself fails due to not sending client certificates then there is no way for the application to even respond with a policy where sending client certificates is allowed.

Browsers behave differently for preflighted cross-origin requests:
- Firefox follows the spec and explicitly won't prompt for a certificate, the preflight will fail and the main request is not performed. There is a bug report for this[3], but as the current behavior is per spec, then the current situation won't be changed unless the spec changes.
- Firefox has recently gotten a new flag, `network.cors_preflight.allow_client_cert`, which *does* send the client certificate to work around this issue[4];
- Chrome (and other Chromium-based browsers like Brave) violate the spec and prompt for a certificate during preflights, thus allowing cross-orign requests on a `client-certificates: required`-servers. There is also a bug report to change this [5].

(Haven't gotten around to testing Safari)

## Argument

Prompt for/send client certificates during preflights.

This should be safe because:
- a certificate is essentially a public key plus metadata, signed by a certificate authority. This data is OK to "leak" because any TLS handshake with client certificates also requires the private key for the certificate to available at the client (not sent over the wire), thus client certificate authentication can't be performed with the sent (leaked) certificate alone.
- an OPTIONS HTTP request (which a preflight is) doesn't cause a server to change state on the resource for which the request is for, so it should be safe to perform, even with credentials.

A security expert should verify the first point.

## Workarounds, discussion

For context, there are workarounds for the current situation for developers/admins if they have control over the implementation of the front- and back-ends. They could:
- change to using `client-certificate: optional` for TLS, so that preflights could get a `Access-Control-Allow-Credentials: true` header in the response.
  - Using this makes the whole setup less secure as now certificates aren't strictly required, and also gives more room to fail at setting it up correctly, as now *some* credential-less requests (= preflights) are allowed while others are not.
- not set any non-safelisted CORS headers,
  - hackish, as you would really like to set whatever headers fit the request
- use HTTP POST with one of the allowed content-types (application/x-www-form-urlencoded multipart/form-data, text/plain) and either encode their data accordingly, or actually send e.g json still, even though the content-type says something else.
  - hackish, as you should be able to use any content-type you need

## References

[1] https://stackoverflow.com/questions/66007054/why-doesnt-cors-preflight-request-prompt-for-or-reuse-a-connection-where-clie
[2] https://fetch.spec.whatwg.org/#cors-protocol-and-credentials
[3] https://bugzilla.mozilla.org/show_bug.cgi?id=1019603
[4] https://bugzilla.mozilla.org/show_bug.cgi?id=1511151
[5] https://bugs.chromium.org/p/chromium/issues/detail?id=775438


-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/fetch/issues/1181

Received on Saturday, 27 February 2021 03:07:41 UTC