You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Drop developer-controlled Authorization header on cross-origin redirects #944
User-agent-controlled credentials are only included for matching requests, but developer-controlled credentials will be copied from request to request.
There's a proposal to scope a developer-controlled Authorization header to the origin of the initial request. (If you use other headers to carry credentials you are out of luck.)
This might be reasonably compatible as Authorization is a header that requires a preflight (and does not allow wildcards) and redirects for preflights were not followed until recently.
What's needed to move this forward:
Implementers need to be interested.
Tests need to be written to ensure it's dropped at the appropriate time (and other headers are not).
(If you use other headers to carry credentials you are out of luck.)
I'm sure there is a good reason for this, so apologies if this has been discussed to death; is there a thread that discusses the motivation of sending custom-set headers by default on redirects to cross-origin domains? The Authorization header seems an obvious candidate for not being sent cross-origin. But, what if folks are doing stuff like MyApp-API-Token: .....? Intentional redirects to third-parties are not super rare, and it seems surprising/a possible foot-gun to relay all custom headers to another origin without the caller opting into that behavior.
@ptoomey3 it's similar to the body being sent there if you use 307/308 (or 301/302 with a method that is not POST). In general the server receiving all the secrets could relay them if they want.
The threat model here seems to be that the server is cooperating but has an XSS of sorts around its Location header handling. I suppose another solution here could be to introduce a new redirect mode, whereby you can only redirect within the origins provided by the request URL and yourself, or some such. (That would also address the request body issue, potential issues with secrets in the request method, etc.)
I think the threat model is a little different here: it's the application interpolating data into URLs of requests to which the Authorization header is attached.
For example, let's say my application takes the user parameter from the query or fragment, and then make a request to /api/{user}/num_frombles. An attacker will be able to set the user to ../../redirect?url=https://evil.example# and make the victim's browser leak their credentials to the attacker's origin (assuming there's a redirector somewhere in the application, which is common.)
These kinds of injections happen fairly frequently because usually by themselves they aren't vulnerabilities, at least as long as the application sanitizes/validates the data it receives from the server. But in a world where Authorization or other headers contain secrets authenticating the user, this can become a major source of problems. It would be nice to fix this, especially if applications haven't started relying on redirected preflights yet.
Even in the non-malicious case...it isn’t that rare (at all) for an application to intentionally redirect to a third party site that isn’t fully trusted. This behavior seems as scary as proposing a site’s cookies should be included in any redirect.
At the (very real) risk of mentioning what people already know, here is some additional information that might be helpful.
A similar problem was reported against curl as CVE-2018-1000007 (a low severity vuln), which they fixed in v7.58.0 by dropping the Authorization header value when redirected to a different host.
Likewise the GO-language http client drops the Authorization header on redirection to a different site. The http library seems to use a slightly different algorithm when deciding when to drop sensitive headers.
That said, this behaviour is not universal: there are clients that (like Fetch API with credentials: "include") will always include the Authorization header when redirected. Similarly, I was unable to find a document describing dropping the Authorization header on redirection as security "best practice". This seems to have grown somewhat organically.
Beyond the security implications, there may be some non-security related problems if the Authorization header is not dropped when redirected to a different host. This is because the redirected host might not be authorised in "the same way" (e.g., the same realm). There's no guarantees that the same username+password (or bearer token, or ...) will work.
Including the Authorization can also break otherwise working solutions. Since the server issuing the redirection cannot (in general) influence the Authorization header the client presents after being redirected, a popular choice is to authorise the subsequent request by embedding a token within the Location URL. If the client, when following the redirection, includes the same Authorization header value then the target service will receive two authorisation: the URL-embeded token and the Authorization header value. An example of this is dynafed when configured to mediate access to some S3 storage. It will react to a GET request (from an authorised client) by generating a pre-signed URL, returning this URL as a redirection. If the client also presents the Dynafed credential (an Authorization header) to the S3 endpoint then the pre-signed URL will not work, as the S3 endpoint favours the Authorization header value over the URL-embedded token.
My own use-case is somewhat similar. On receiving a GET request, dCache will generate a single-use bearer token to authorise a specific file transfer and respond with a redirection to a URL that embeds this token in the URL. We currently support both encrypted (with TLS) and unencrypted data transfers. We feel there are situations where this is acceptable to send the single-use bearer token over an unencrypted connection; however, this becomes problematic if the client sends a long-lived credential. Using only TLS protected redirection would be possible if we had a reliable way to detect web-browsers using the Fetch API.
If I may venture an opinion, "Atomic HTTP redirect handling" is a stated goal of Fetch, which places the onus on the API (and the web-browser implementations) to "do the right thing". The current behaviour for credentials: 'include' seems to go against perceived security best practice and would seem to have the possibility to break (seemingly) valid use-cases.
I encountered an issue with this behaviour that struck me as counter-intuitive, but reading through this issue I can see why it occurred. As @paulmillar states above, consider a use-case of an API requiring authentication that if successful redirects to a cloud resource using a pre-signed URL (e.g. S3, GCP, other bucket storage) derived from server-side secrets:
The following looks like it should restrict the Authorization header to the same origin:
// Execute a fetch against a same-origin API endpointfetch('/api',{credentials: 'same-origin',headers: {'Authorization': 'Bearer a_secret_value'}}).then(res=>/* Do something with data from transparent redirect to cloud provider */)
In the case of S3 with a permissive CORS policy, the actual error returned is that only one authentication method can be used, i.e. S3 saw a signed URL and an Authorization header both of which could be valid in different circumstances, however the header in this case was only valid for the same origin API server. If I block the Authorization header in the CORS preflight response, the request fails anyway because fetch still tries to send it.
As a developer, based on the semantics of credentials: 'same-origin', I would not expect my API token to be forwarded on to a different origin. I would also not expect to have to configure a third party provider to reject my secret header.
I am also unable to manually handle this edge case by intercepting the redirect, based on the answers to Issue #763.
If I may venture an opinion, "Atomic HTTP redirect handling" is a stated goal of Fetch, which places the onus on the API (and the web-browser implementations) to "do the right thing". The current behaviour for credentials: 'include' seems to go against perceived security best practice and would seem to have the possibility to break (seemingly) valid use-cases.
I would suggest that it is the behaviour for credentials: 'same-origin' that is against perceived best practice here, and does break my seemingly valid use-case.
Reacted by pumelo, Paul Millar, Mark Garringer, Tomáš Linhart, TJ Challstrom and Lunan Li
Credentials are HTTP cookies, TLS client certificates, and authentication entries (for HTTP authentication). [COOKIES] [TLS] [HTTP-AUTH]
The [HTTP-AUTH] reference defines a number of header fields that could be encompassed by the term 'credentials': WWW-Authenticate, Authorization, Proxy-Authenticate, and Proxy-Authorization. Therefore it seems like any use of the term 'credentials' in the spec would be expected to encompass certain specific headers. Truly custom headers as pointed out by @ptoomey3 would not be treated as credentials in any case, and security concerns aside would at least be far less likely cause a collision of authentication methods at the redirection target, by virtue of being somewhat namespaced.
If the intention behind the implementation of fetch is that the handling of credentials only applies to when these are automatically populated by the browser (e.g. cookies), then I feel this should be explicit. However it does leave a gap/bug in the implementation for being able to handle the above use-case.
Either, though I'd be somewhat reluctant to remove them same-origin as well. On the flipside, considering this anew it does seem like a weird special case for Authorization. It's justifiable, but it doesn't make for a coherent security story.
Activity
ptoomey3 commentedon Oct 1, 2019
I'm sure there is a good reason for this, so apologies if this has been discussed to death; is there a thread that discusses the motivation of sending custom-set headers by default on redirects to cross-origin domains? The
Authorizationheader seems an obvious candidate for not being sent cross-origin. But, what if folks are doing stuff likeMyApp-API-Token: .....? Intentional redirects to third-parties are not super rare, and it seems surprising/a possible foot-gun to relay all custom headers to another origin without the caller opting into that behavior.annevk commentedon Dec 3, 2019
@ptoomey3 it's similar to the body being sent there if you use 307/308 (or 301/302 with a method that is not POST). In general the server receiving all the secrets could relay them if they want.
The threat model here seems to be that the server is cooperating but has an XSS of sorts around its
Locationheader handling. I suppose another solution here could be to introduce a new redirect mode, whereby you can only redirect within the origins provided by the request URL and yourself, or some such. (That would also address the request body issue, potential issues with secrets in the request method, etc.)arturjanc commentedon Jan 25, 2020
I think the threat model is a little different here: it's the application interpolating data into URLs of requests to which the
Authorizationheader is attached.For example, let's say my application takes the
userparameter from the query or fragment, and then make a request to/api/{user}/num_frombles. An attacker will be able to set theuserto../../redirect?url=https://evil.example#and make the victim's browser leak their credentials to the attacker's origin (assuming there's a redirector somewhere in the application, which is common.)These kinds of injections happen fairly frequently because usually by themselves they aren't vulnerabilities, at least as long as the application sanitizes/validates the data it receives from the server. But in a world where
Authorizationor other headers contain secrets authenticating the user, this can become a major source of problems. It would be nice to fix this, especially if applications haven't started relying on redirected preflights yet.@mikewest
ptoomey3 commentedon Jan 25, 2020
Even in the non-malicious case...it isn’t that rare (at all) for an application to intentionally redirect to a third party site that isn’t fully trusted. This behavior seems as scary as proposing a site’s cookies should be included in any redirect.
paulmillar commentedon May 4, 2021
At the (very real) risk of mentioning what people already know, here is some additional information that might be helpful.
A similar problem was reported against curl as CVE-2018-1000007 (a low severity vuln), which they fixed in v7.58.0 by dropping the
Authorizationheader value when redirected to a different host.Similarly, the popular Python HTTP client Requests will drop the
Authorizationheader values "if you get redirected off-host".Likewise the GO-language http client drops the
Authorizationheader on redirection to a different site. The http library seems to use a slightly different algorithm when deciding when to drop sensitive headers.That said, this behaviour is not universal: there are clients that (like Fetch API with
credentials: "include") will always include theAuthorizationheader when redirected. Similarly, I was unable to find a document describing dropping theAuthorizationheader on redirection as security "best practice". This seems to have grown somewhat organically.Beyond the security implications, there may be some non-security related problems if the
Authorizationheader is not dropped when redirected to a different host. This is because the redirected host might not be authorised in "the same way" (e.g., the same realm). There's no guarantees that the same username+password (or bearer token, or ...) will work.Including the
Authorizationcan also break otherwise working solutions. Since the server issuing the redirection cannot (in general) influence theAuthorizationheader the client presents after being redirected, a popular choice is to authorise the subsequent request by embedding a token within theLocationURL. If the client, when following the redirection, includes the sameAuthorizationheader value then the target service will receive two authorisation: the URL-embeded token and theAuthorizationheader value. An example of this is dynafed when configured to mediate access to some S3 storage. It will react to a GET request (from an authorised client) by generating a pre-signed URL, returning this URL as a redirection. If the client also presents the Dynafed credential (anAuthorizationheader) to the S3 endpoint then the pre-signed URL will not work, as the S3 endpoint favours theAuthorizationheader value over the URL-embedded token.My own use-case is somewhat similar. On receiving a GET request, dCache will generate a single-use bearer token to authorise a specific file transfer and respond with a redirection to a URL that embeds this token in the URL. We currently support both encrypted (with TLS) and unencrypted data transfers. We feel there are situations where this is acceptable to send the single-use bearer token over an unencrypted connection; however, this becomes problematic if the client sends a long-lived credential. Using only TLS protected redirection would be possible if we had a reliable way to detect web-browsers using the Fetch API.
If I may venture an opinion, "Atomic HTTP redirect handling" is a stated goal of Fetch, which places the onus on the API (and the web-browser implementations) to "do the right thing". The current behaviour for
credentials: 'include'seems to go against perceived security best practice and would seem to have the possibility to break (seemingly) valid use-cases.pool: http allow client to send credentials when TLS is used
pool: http allow client to send credentials when TLS is used
pool: http allow client to send credentials when TLS is used
pool: http allow client to send credentials when TLS is used
pool: http allow client to send credentials when TLS is used
6 remaining items
pool: http allow client to send credentials when TLS is used
pool: http allow client to send credentials when TLS is used
richjyoung commentedon May 13, 2021
I encountered an issue with this behaviour that struck me as counter-intuitive, but reading through this issue I can see why it occurred. As @paulmillar states above, consider a use-case of an API requiring authentication that if successful redirects to a cloud resource using a pre-signed URL (e.g. S3, GCP, other bucket storage) derived from server-side secrets:
The following looks like it should restrict the
Authorizationheader to the same origin:In the case of S3 with a permissive CORS policy, the actual error returned is that only one authentication method can be used, i.e. S3 saw a signed URL and an Authorization header both of which could be valid in different circumstances, however the header in this case was only valid for the same origin API server. If I block the
Authorizationheader in the CORS preflight response, the request fails anyway because fetch still tries to send it.As a developer, based on the semantics of
credentials: 'same-origin', I would not expect my API token to be forwarded on to a different origin. I would also not expect to have to configure a third party provider to reject my secret header.I am also unable to manually handle this edge case by intercepting the redirect, based on the answers to Issue #763.
I would suggest that it is the behaviour for
credentials: 'same-origin'that is against perceived best practice here, and does break my seemingly valid use-case.richjyoung commentedon May 13, 2021
https://fetch.spec.whatwg.org/#credentials:
The [HTTP-AUTH] reference defines a number of header fields that could be encompassed by the term 'credentials':
WWW-Authenticate,Authorization,Proxy-Authenticate, andProxy-Authorization. Therefore it seems like any use of the term 'credentials' in the spec would be expected to encompass certain specific headers. Truly custom headers as pointed out by @ptoomey3 would not be treated as credentials in any case, and security concerns aside would at least be far less likely cause a collision of authentication methods at the redirection target, by virtue of being somewhat namespaced.If the intention behind the implementation of fetch is that the handling of credentials only applies to when these are automatically populated by the browser (e.g. cookies), then I feel this should be explicit. However it does leave a gap/bug in the implementation for being able to handle the above use-case.
annevk commentedon Mar 2, 2022
It seems that Safari drops
Authorizationon the first redirect (whatever the origins involved), which suggests this is feasible.@yutakahirano @youennf @ddragana thoughts?
yutakahirano commentedon Mar 22, 2022
Do you mean removing
authorizationon every redirect by "this"? Or do you mean removing it only on cross-origin redirects?annevk commentedon Mar 22, 2022
Either, though I'd be somewhat reluctant to remove them same-origin as well. On the flipside, considering this anew it does seem like a weird special case for
Authorization. It's justifiable, but it doesn't make for a coherent security story.sideshowbarker commentedon May 2, 2022
For the record here:
youennf commentedon May 18, 2022
FWIW, shipping Safari is now preserving the Authorization header for same origin redirections.
paulmillar commentedon May 18, 2022
@youennf Do you happen to know whether, with this change, Safari drops the Authorization header with different origin redirections?
youennf commentedon May 18, 2022
From code inspection, the header is dropped in case of cross-origin redirection
Remove Authorization header upon cross-origin redirect
Remove Authorization header upon cross-origin redirect