Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set explicit Origin in CORS preflight response if allow_credentials is True and allow_origins is wildcard #2

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

jcwilson
Copy link
Owner

@jcwilson jcwilson commented Nov 26, 2020

When making a preflight request, the browser provides no indication as to whether the actual subsequent
CORS request will pass up credentials (read: cookies). However, unless the preflight response explicitly allows
the request's Origin in the Access-Control-Response-Header, the browser will fail the CORS check and
prevent the actual follow-up CORS request. This means that responding to preflight requests with the *
wildcard when allow_credentials=True is not sufficient to allow preflighted credentialed requests.

The current workaround is to provide an equivalently permissive allow_origin_regex pattern, but this would
appear to be a POLA violation. If one wishes to allow calls from all origins, regardless of the allow_credentials
setting, then allow_origins=["*"] should be sufficient to express that intent.

The simple_response() code already performs similar logic which currently only applies to non-preflighted
requests since the browser would never make a preflighted request that hits said code due to this issue:

# If request includes any cookie headers, then we must respond
# with the specific origin instead of '*'.
if self.allow_all_origins and has_cookie:
    headers["Access-Control-Allow-Origin"] = origin

This PR just brings the two halves inline with each other.

See the relevant MDN CORS docs here:

When used as part of a response to a preflight request, this indicates whether or not the actual request can
be made using credentials. Note that simple GET requests are not preflighted, and so if a request is made for
a resource with credentials, if this header is not returned with the resource, the response is ignored by the
browser and not returned to web content.

Note: This could be considered an interface-breaking change and demand a stronger version bump if starlette
follows a strict semver policy.
Servers that that currently specify allow_origins="*" instruct the browser to
block credentialed CORS calls that required preflight authorization. Adopting this behavior would allow these
calls through and could result in an unexpected relaxation of their current effective security rules. However, it
looks like starlette is still pre-1.0.0, so maybe this just means a minor version bump?

@jcwilson jcwilson force-pushed the cors-preflight-allow-credentials branch from 14fae82 to 4b1fc4e Compare November 26, 2020 11:21
@jcwilson jcwilson changed the title Allow explicit Origin in preflight response if credentials allowed [CORS] Allow explicit Origin in preflight response if credentials allowed Nov 26, 2020
@jcwilson jcwilson changed the title [CORS] Allow explicit Origin in preflight response if credentials allowed Allow explicit Origin in CORS preflight response if credentials allowed Nov 27, 2020
@jcwilson jcwilson changed the title Allow explicit Origin in CORS preflight response if credentials allowed Set explicit Origin in CORS preflight response if allow_credentials is True and allow_origins is wildcard Nov 27, 2020
@jcwilson jcwilson force-pushed the cors-preflight-allow-credentials branch from ece46e3 to f3ab6a6 Compare December 11, 2020 10:02
Josh Wilson added 2 commits December 11, 2020 02:21
…s True and allow_origins is wildcard

When making a preflight request, the browser makes no indication as to whether the actual subsequent
request will pass up credentials. However, unless the preflight response explicitly allows the
request's `Origin` in the `Access-Control-Response-Header`, the browser will fail the CORS check and
prevent the actual follow-up CORS request. This means that responding with the `*` wildcard is not
sufficient to allow preflighted credentialed requests. The current workaround is to provide an
equivalently permissive `allow_origin_regex` pattern.

The `simple_response()` code already performs similar logic which currently only applies to
non-preflighted requests since the browser would never make a preflighted request that hits this
code due to this issue:

```
if self.allow_all_origins and has_cookie:
    headers["Access-Control-Allow-Origin"] = origin
```

This just bring the two halves inline with each other.
@jcwilson jcwilson force-pushed the cors-preflight-allow-credentials branch from f3ab6a6 to 1a28cce Compare December 11, 2020 10:22
jcwilson and others added 7 commits April 3, 2021 22:50
This simplifies the code slightly by using this recently added method.

It has some trade-offs, though. We now construct a `MutableHeaders` instead of a simple `dict` when
copying the pre-computed preflight headers, and we move the `Vary` header construction out of the
pre-computation and into the call handler.

I think it makes the code more maintainable and the added per-call computation is minimal.
This also names and caches some of the boolean tests in __init__() which we use in later if-blocks.
This follows the existing pattern in order to better self-document the code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
1 participant