POST with basic Auth to CORS is blocked with no explanation
Categories
(Core :: DOM: Networking, defect, P3)
Tracking
()
Tracking | Status | |
---|---|---|
firefox110 | --- | fixed |
People
(Reporter: utopiabound, Assigned: valentin)
References
(Blocks 3 open bugs)
Details
(Whiteboard: [necko-triaged])
Attachments
(1 file)
User Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:93.0) Gecko/20100101 Firefox/93.0
Steps to reproduce:
Use Case: Code flow authentication via Oauth2 to reddit.com via javascript
Tracking Protection is disabled in the browser.
POST to a remote site (https://www.reddit.com/api/v1/access_token) with "Authorization: Basic "+btoa(ID+":") set in the header.
Actual results:
DevTools shows "Blocked" in "Transferred" for the POST, there is not OPTIONS prior as one would expect for a CORS lookup.
There is no explanation for why it was blocked.
Expected results:
The POST should have send the data to the server and received json data back. This works in Chrome and Safari.
The same misbehavior is observed in Firefox for iOS, but Safari on the same device works correctly.
Comment 1•3 years ago
|
||
The Bugbug bot thinks this bug should belong to the 'Core::Privacy: Anti-Tracking' component, and is moving the bug to that component. Please revert this change in case you think the bot is wrong.
Updated•3 years ago
|
Reporter | ||
Comment 2•3 years ago
|
||
Correction: Firefox of iOS does not exhibit this behavior, it functions like Safari, and loads the endpoint correctly (working in Firefox Daylight 39.0).
Comment 3•3 years ago
|
||
All browsers on iOS are webkit (safari) underneath. Apple rules do not allow any other "web engine", only different browser features built on top.
POST how? an actual form? XMLHttpRequest? fetch()? what options are you using in the call? Do you have a site or testcase to demonstrate this? Even just a snippet of the relevant code?
Reporter | ||
Comment 4•3 years ago
|
||
https://redditp.utopiabound.net - then login to reddit: little person in bottom left > little person icon. This runs through the "Code flow" OAuth2 token retrieval (https://github.com/reddit-archive/reddit/wiki/OAuth2#token-retrieval-code-flow) which fails on "Retrieving the access token".
The actual access is:
var jsonUrl = 'https://www.reddit.com/api/v1/access_token';
var data = {
grant_type: 'authorization_code',
code: code,
redirect_uri: rp.redirect
};
$.ajax({
url: jsonUrl,
method: 'POST',
data: data,
dataType: 'json',
headers: {
"Authorization": "Basic " + btoa(rp.api_key.reddit + ":")
},
username: rp.api_key.reddit,
password: '',
success: handleData,
error: handleError,
timeout: rp.settings.ajaxTimeout,
crossDomain: true
});
Comment 5•3 years ago
|
||
I can reproduce this, thanks for filing. I think this is something we probably want to figure out soon.
Christoph, do you know off the top of your head if the username prefix in the URL (https://foo@bar.com) would cause us to misdiagnose the request as violating a cross-origin policy?
Tentatively marking as P2 until we understand this better.
Comment 6•3 years ago
|
||
Per Fetch a 401 response is required before another request is made that will include the Authorization
header based on the credentials included in the URL. However, if you get such a 401 response it will not be handled automatically for CORS requests.
What might happen is that other browsers unconditionally include the Authorization
header based on the credentials included in the URL. That would go against the specification, but test coverage is not great I believe.
(I also seem to remember that including credentials was not allowed at all for CORS requests, but it seems that only redirects end up breaking if the Location
header value contains them. It still breaks because of the 401 response dance, but I thought there was something more explicit that I cannot find now.)
Comment 7•3 years ago
|
||
(In reply to Nihanth Subramanya [:nhnt11] from comment #5)
Christoph, do you know off the top of your head if the username prefix in the URL (https://foo@bar.com) would cause us to misdiagnose the request as violating a cross-origin policy?
The short answer is No, it shouldn't, we should only compare scheme, host, port
for performing same-origin checks.
The longer answer and probably helpful for debugging:
- Our CORS Code gets entered within nsCORSListenerProxy::UpdateChannel
- which then consults nsScriptSecurityManager::CheckLoadURIWithPrincipal
- which eventually passes through ContentPrincipal::MayLoadInternal
- and ultimately should end up comparing URIs within NS_SecurityCompareURIs() which only compares
scheme, host and port
of URI.
Reporter | ||
Comment 10•3 years ago
|
||
Any ETA for this?
This is still broken in 99.0.1
Reporter | ||
Comment 11•3 years ago
|
||
I've poked this a bit more and found that I have a workaround for this issue, if I replace the above .ajax call with this:
$.ajax({
url: jsonUrl,
method: 'POST',
data: data,
dataType: 'json',
headers: {
"Authorization": "Basic " + btoa(rp.api_key.reddit + ":")
},
success: handleData,
error: handleError,
timeout: rp.settings.ajaxTimeout,
crossDomain: true
});
It will do OPTIONS
to jsonUrl, and then a POST
to jsonUrl with Authorization:
header and get back correct keys and run happily.
With the username:
and password:
set (with or without the manual headers:
settings) in the above example, it fails to do the OPTIONS
and just reports "blocked" for the POST
operation.
Assignee | ||
Comment 12•2 years ago
|
||
$.ajax does XHR, but this may also affect to fetch.
Comment 13•2 years ago
|
||
I just happened to see this bug and the code, hope that this helps:
It seems that this part of nsCORSListenerProxy::UpdateChannel
may potentially be causing this bug:
nsCString userpass;
uri->GetUserPass(userpass);
NS_ENSURE_TRUE(userpass.IsEmpty(), NS_ERROR_DOM_BAD_URI);
If I read the code correctly, then the presence of a username/password component in the requested URL immediately causes a CORS failure without extra logging (no LogBlockedRequest
call).
Assignee | ||
Comment 14•2 years ago
|
||
Thank you for pointing us to that code, Rob.
I tried disabling that check in a try push but it starts failing a bunch of WPT.
Judging by the failing tests we ought to block cross-origin redirects to a URL with credentials, but we also apply this to any cross-origin URI.
Assignee | ||
Comment 15•2 years ago
|
||
All of the WPT are checking that cross origin redirects to URLs with
user:password are blocked, but Firefox was blocking any CORS request
to a URL with user:password.
Related WPT:
fetch/api/cors/cors-redirect-credentials.any.js
fetch/security/redirect-to-url-with-credentials.https.html
cors/redirect-userinfo.htm
service-workers/service-worker/fetch-event-redirect.https.html
xhr/access-control-and-redirects-async.any.html
Updated•2 years ago
|
Assignee | ||
Comment 16•2 years ago
|
||
(In reply to Anne (:annevk) from comment #6)
(I also seem to remember that including credentials was not allowed at all for CORS requests, but it seems that only redirects end up breaking if the
Location
header value contains them. It still breaks because of the 401 response dance, but I thought there was something more explicit that I cannot find now.)
Anne, I'm unable to locate the part of the spec that indicates CORS redirects with user:password should fail. Do you happen to know where that is?
Comment 17•2 years ago
|
||
Assignee | ||
Comment 18•2 years ago
|
||
Thank you, Anne! 🙏
Comment 19•2 years ago
|
||
Comment 20•2 years ago
|
||
Backed out for causing mochitest failures on test_CrossSiteXHR.html.
[task 2023-01-11T16:19:56.355Z] 16:19:56 INFO - TEST-PASS | dom/security/test/cors/test_CrossSiteXHR.html | wrong responseText in test for {"pass":1,"method":"GET","noAllowPreflight":1}
[task 2023-01-11T16:19:56.355Z] 16:19:56 INFO - Buffered messages finished
[task 2023-01-11T16:19:56.356Z] 16:19:56 INFO - TEST-UNEXPECTED-FAIL | dom/security/test/cors/test_CrossSiteXHR.html | should have failed in test for {"pass":0,"method":"GET","noAllowPreflight":1,"username":"user"} - got false, expected true
[task 2023-01-11T16:19:56.356Z] 16:19:56 INFO - SimpleTest.is@SimpleTest/SimpleTest.js:487:14
[task 2023-01-11T16:19:56.357Z] 16:19:56 INFO - runTest@dom/security/test/cors/test_CrossSiteXHR.html:759:9
[task 2023-01-11T16:19:56.357Z] 16:19:56 INFO - initTestCallback/<@dom/security/test/cors/test_CrossSiteXHR.html:39:9
[task 2023-01-11T16:19:56.357Z] 16:19:56 INFO - Not taking screenshot here: see the one that was previously logged
[task 2023-01-11T16:19:56.359Z] 16:19:56 INFO - TEST-UNEXPECTED-FAIL | dom/security/test/cors/test_CrossSiteXHR.html | wrong status in test for {"pass":0,"method":"GET","noAllowPreflight":1,"username":"user"} - got 200, expected +0
[task 2023-01-11T16:19:56.359Z] 16:19:56 INFO - SimpleTest.is@SimpleTest/SimpleTest.js:487:14
[task 2023-01-11T16:19:56.359Z] 16:19:56 INFO - runTest@dom/security/test/cors/test_CrossSiteXHR.html:761:9
[task 2023-01-11T16:19:56.359Z] 16:19:56 INFO - initTestCallback/<@dom/security/test/cors/test_CrossSiteXHR.html:39:9
[task 2023-01-11T16:19:56.360Z] 16:19:56 INFO - Not taking screenshot here: see the one that was previously logged
[task 2023-01-11T16:19:56.361Z] 16:19:56 INFO - TEST-UNEXPECTED-FAIL | dom/security/test/cors/test_CrossSiteXHR.html | wrong status text for {"pass":0,"method":"GET","noAllowPreflight":1,"username":"user"} - got "OK", expected ""
[task 2023-01-11T16:19:56.361Z] 16:19:56 INFO - SimpleTest.is@SimpleTest/SimpleTest.js:487:14
[task 2023-01-11T16:19:56.361Z] 16:19:56 INFO - runTest@dom/security/test/cors/test_CrossSiteXHR.html:762:9
[task 2023-01-11T16:19:56.362Z] 16:19:56 INFO - initTestCallback/<@dom/security/test/cors/test_CrossSiteXHR.html:39:9
[task 2023-01-11T16:19:56.363Z] 16:19:56 INFO - Not taking screenshot here: see the one that was previously logged
[task 2023-01-11T16:19:56.364Z] 16:19:56 INFO - TEST-UNEXPECTED-FAIL | dom/security/test/cors/test_CrossSiteXHR.html | wrong responseXML in test for {"pass":0,"method":"GET","noAllowPreflight":1,"username":"user"} - got "<res>hello pass</res>", expected null
[task 2023-01-11T16:19:56.365Z] 16:19:56 INFO - SimpleTest.is@SimpleTest/SimpleTest.js:487:14
<...>
Assignee | ||
Updated•2 years ago
|
Comment 21•2 years ago
|
||
Comment 22•2 years ago
|
||
bugherder |
Description
•