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

Cannot get next URL for redirect="manual" #763

Closed
askoretskiy opened this issue Jun 14, 2018 · 26 comments · Fixed by #1010
Closed

Cannot get next URL for redirect="manual" #763

askoretskiy opened this issue Jun 14, 2018 · 26 comments · Fixed by #1010
Labels
clarification Standard could be clearer good first issue Ideal for someone new to a WHATWG standard or software project

Comments

@askoretskiy
Copy link

When you use redirect="manual" there is not way to get the new URL, i.e. to where response was redirected.

Example for Firefox:

fetch("/account/", {
    credentials: 'same-origin',
    redirect: 'manual',
}).then(response => {
    console.log(response);
});

Server would response with redirect to /login/?next=/account/.

Here is Javascript Response object:

{
bodyUsed: false,
headers: {},
ok: false,
redirected: false,
status: 0,
​​statusText: "",
​​type: "opaqueredirect",
​​url: "http://localhost:8000/account/"
}

So response.url is the URL I requested.

How could I get the URL where response was redirected to? There is no "redirect_url" and headers are empty.

As far as I understand the specification, with "manual" is meant to handle redirects manually. Then there should be a way to get this new URL and do another request to this new URL.

Here is what server send:

HTTP/1.1 302 Found
X-Powered-By: Express
date: Thu, 14 Jun 2018 09:41:49 GMT
server: WSGIServer/0.2 CPython/3.7.0b5
content-type: text/html; charset=utf-8
location: /login/?next=/account/
x-frame-options: SAMEORIGIN
content-length: 0
vary: Accept-Language, Cookie
content-language: en
connection: keep-alive

How to access this new url /login/?next=/account/?

@annevk
Copy link
Member

annevk commented Jun 14, 2018

That is by design: https://fetch.spec.whatwg.org/#atomic-http-redirect-handling. This feature is useful in combination with service workers.

@annevk annevk closed this as completed Jun 14, 2018
@askoretskiy
Copy link
Author

Maybe documentation of “redirect” block should be extended to reduce confusion? For example, a link to the url you provided?

What about renaming “manual” to false or “disabled” or “no-follow”? Current name gives wrong expectations, since you cannot handle redirects manually. It just do not follow, indicate it got redirect and do not raise an error.

@annevk
Copy link
Member

annevk commented Jun 14, 2018

I wouldn't mind a link. It might also help to state that it's primarily for handling navigation requests (which do redirects manually in a sense) in service workers.

Unfortunately renaming this at this point is too late as browsers have shipped the functionality, but I do agree that we made a bad call there. Mea culpa!

@annevk annevk reopened this Jun 14, 2018
@annevk annevk added good first issue Ideal for someone new to a WHATWG standard or software project clarification Standard could be clearer labels Jun 14, 2018
@askoretskiy
Copy link
Author

👍

@askoretskiy
Copy link
Author

askoretskiy commented Jun 14, 2018

By the way, it would be great if provide an example how redirect=“manual” could be used with service workers for navigation. At least an idea.

Did you mean service workers that handle browser requests so that we app e. g. could work offline?

That would be helpful for devs.

@annevk
Copy link
Member

annevk commented Jun 14, 2018

Say the user navigates to /test. The service worker for that scope intercepts the fetch and forwards it to the server. The server responds with a redirect to /somewhere-else. If the service worker returns that redirect the browser will then navigate to /somewhere-else`, potentially hitting the same service worker or another. (The service worker could store these responses so this kind of redirect would also function offline.)

Note that typically redirects are followed for fetches (e.g., from <img>, <script>, fetch() by default), but navigation explicitly doesn't want to follow redirects because we might want to ask a different service worker to handle the request as the scope or origin can change.

Note that the browser does get to see the contents of the redirect, but nobody else does per the aforementioned link.

@wanderview
Copy link
Member

Hmm, I have to admit I thought someone directly calling fetch(url, { redirect: "manual" }) from the main thread would be able to walk the redirect chain themselves. I guess I was confused when we added the manual redirect stuff.

@annevk
Copy link
Member

annevk commented Jun 14, 2018

We could offer that if the redirect response opts out of being opaque as per #601.

@Radiergummi
Copy link

So, excuse me for digging out this old issue, but I'd just like to confirm this assumption: There is currently no way to get a hold of the Location header if a response has been redirected? This is quite annoying when implementing progressively enhanced login forms - I would have liked to capture form submissions, show errors on failure or redirect manually on success.

@annevk
Copy link
Member

annevk commented Feb 23, 2019

That is correct, we could perhaps add an opt-in header for that as per #601.

@tonyhb
Copy link

tonyhb commented Apr 16, 2020

Echoing my comment in #601, its really surprising that the "manual" redirect mode is neither manual nor can you get the location. Can we get the location from a redirect?

There are multiple scenarios that you may want this, whether it's checking redirect URLs programatically or changing headers before redirects.

pparidans added a commit to odoo-dev/odoo that referenced this issue Aug 19, 2020
This commit adds the prefetch of the current page's children (cf. under
the same scope) to be available for offline use.

For performance reason, only the first-level children are added to the
cache to avoid a huge amount of requests and/or resources used by simply
loading the event website's pages. Also, for the same reason, this
process is delegated to the ServiceWorker to avoid cluttering the Main
Thread.

Note: due to some security restriction introduced in the Fetch spec,
some redirects may prevent offline-requests from being properly
fulfilled from the cache. See references for further details.

References:
- whatwg/fetch#763
- GoogleChromeLabs/sw-precache#220
- https://fetch.spec.whatwg.org/#concept-request-redirect-mode
robodoo pushed a commit to odoo/odoo that referenced this issue Aug 19, 2020
This commit adds the prefetch of the current page's children (cf. under
the same scope) to be available for offline use.

For performance reason, only the first-level children are added to the
cache to avoid a huge amount of requests and/or resources used by simply
loading the event website's pages. Also, for the same reason, this
process is delegated to the ServiceWorker to avoid cluttering the Main
Thread.

Note: due to some security restriction introduced in the Fetch spec,
some redirects may prevent offline-requests from being properly
fulfilled from the cache. See references for further details.

References:
- whatwg/fetch#763
- GoogleChromeLabs/sw-precache#220
- https://fetch.spec.whatwg.org/#concept-request-redirect-mode

closes #56058

Signed-off-by: Adrien Dieudonné (adr) <adr@odoo.com>
@ray007
Copy link

ray007 commented Jul 27, 2021

Having a manual mode and no way retrieve the target and actually do the work manually sounds like a bug.
Should this issue really be closed?

@annevk
Copy link
Member

annevk commented Jul 27, 2021

Nothing changed since #763 (comment).

@iamnoah
Copy link

iamnoah commented Aug 12, 2021

Just wanted to chime in as a user who appreciates atomic redirects. My use case is redirecting an authenticated CORS request. CORS will not forward the Auth header a second time, so we have to add a token to the redirect Location. If the Location could be accessed via JavaScript, that could open us up to credential stealing XSS (maybe not directly in this case since CORS, but defense-in-depth is important.)

@Kukunin
Copy link

Kukunin commented Sep 9, 2021

@iamnoah do you pass the token to the redirect Location via GET params? Are you concerned with the fact that it will be both visible and logged everywhere in access logs (user proxies, web servers, potentially routers, etc)

@iamnoah
Copy link

iamnoah commented Sep 9, 2021

@iamnoah do you pass the token to the redirect Location via GET params? Are you concerned with the fact that it will be both visible and logged everywhere in access logs (user proxies, web servers, potentially routers, etc)

No, because it’s over https, so anything that can see the full url also can see normal cookies. The tokens are short lived.

The real concern is XSS or something being able to make a request and get the token and immediately use it for an automated attack.

@Yay295
Copy link

Yay295 commented Sep 9, 2021

Can you use HttpOnly? That should prevent XSS.

@Kukunin
Copy link

Kukunin commented Sep 10, 2021

@iamnoah I would argue that URLs are as safe as cookies over https. Yes, middlemen are off, but there are still might be sniffers on the client end, for example, https://securitywithsam.com/2019/07/dataspii-leak-via-browser-extensions/. I remember a story (couldn't find proof though) that an extension from a search engine caused unlisted public URLs indexed in the search engine - this way a lot of private documents were leaked.

I agree, that it's ok with short-living tokens, but I would still be careful to pass something sensitive over GET params in URLs

@danvau7
Copy link

danvau7 commented Jun 2, 2022

Why can't a user / program verify where the redirect will take them? Say I want to block all script that originate from 44.0.0.0/8.

I would need to determine where that redirect will go, followed by a DNS lookup to see what the A record is, and then see if it is in the 44.0.0.0/8 range.

I apologize if I have misunderstood anything here.

@KieranP
Copy link

KieranP commented Feb 15, 2023

Ran into the same issue today. Had an API login endpoint return 303 when we wanted the browser to do a full page redirect. Obviously we didn't want the fetch request doing the redirect itself, so set redirect to manual, expecting to be able to fetch the Location header from the 303 response. And then struggled to figure out why we couldn't for an hour until finally stumbling on this ticket. So we ended up changing our 303's into 204's with a Location header instead, which isn't correct but works. I would recommend a rethink of not having the Location header in the 303 response for manual redirections. It is confusing behaviour. At the very least, browsers should show a message pointing to this ticket if users try to access the headers of the Response object.

@codekandis
Copy link

codekandis commented Feb 15, 2023

That is by design: https://fetch.spec.whatwg.org/#atomic-http-redirect-handling. This feature is useful in combination with service workers.

There's an example used to explain a security risk. One simply can use curl to get the URI with that secret mentioned. So this measurement is quiet useless.

The server application can respond with secrets in such various ways without any redirect. Omitting the data of the redirects is just a drop in the bucket to prevent security leaks of server applications.

Therefore the server application must be hardened. In the most cases the server application is my application. But in fact in a browser and JavaScript context GET request can go anywhere to any 3rd-party server application in the WWW. I (the client) am not responsible for their security issues.

It's not the client's responsibility. Any security measurements of the client's code should affect the client's security and not the server's one. But that mentioned specification ignores that fact at user's expense.

I have use cases where I must prevent an auto-redirect while I have to set various additional headers and options before the redirect will be made. Omitting the redirect data makes it unpossible.

@annevk
Copy link
Member

annevk commented Feb 15, 2023

The point is that you cannot get the user to use curl and tell you what they see as that might well be different. That's often misunderstood when it comes to the same-origin policy.

@KieranP DevTools making this clearer makes sense to me. Please file bugs against browsers directly for that. At this point we cannot change the naming as that would break websites.

@artem-skoretskiy-motesque-com

If we talk about security considerations, why not let CORS policy deal with this?

The example pointed on https://fetch.spec.whatwg.org/#atomic-http-redirect-handling is useless. With the same regard I could say redirect: "follow" and then extract any secret information from this response:

fetch("/account/", {
    credentials: 'same-origin',
    redirect: 'follow',
}).then((response) => {
    console.log("Here is my secret URL", response.url);
})

That would not work for multi-step redirects (original page -> secret URL -> target public page).

@annevk
Copy link
Member

annevk commented Feb 15, 2023

The same-origin policy (including CORS) does deal with this, which is why the example uses a different origin for the eventual target. You don't always get to see the final response (or its URL) after all. You are correct that in practice there are often more redirects involved, but the example is sound.

@ray007
Copy link

ray007 commented Feb 15, 2023

CORS is one of the worst inventions ever for the web.
It converted the web from its default "share data unless prohibited" to "do not share unless the admin works to allow it". At the cost of extra requests. Evaluated client-side.

@annevk
Copy link
Member

annevk commented Feb 15, 2023

I'm going to lock this thread as this is going nowhere and this issue was resolved long ago, but CORS only ever enabled more sharing between resources than was possible before. I recommend looking into the same-origin policy if you are looking for something to blame.

@whatwg whatwg locked as resolved and limited conversation to collaborators Feb 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
clarification Standard could be clearer good first issue Ideal for someone new to a WHATWG standard or software project
Development

Successfully merging a pull request may close this issue.