Open Bug 1459321 Opened 6 years ago Updated 5 months ago

Treat loads the result from location.reload() as samesite

Categories

(Core :: DOM: Security, enhancement, P3)

61 Branch
enhancement

Tracking

()

Tracking Status
firefox61 --- affected

People

(Reporter: April, Unassigned)

References

(Depends on 1 open bug, Blocks 1 open bug)

Details

(Whiteboard: [domsecurity-backlog1])

I've found some behavior with SameSite cookies that differs between Chrome and Firefox. It's not entirely clear from the specification what the behavior should be.

✅ Step 1: Visit https://samesite-strict.badsite.io/ (SameSite=Strict is set)
✅ Step 2: Visit https://samesite.badsite.cross-origin.io/ (cross-origin cookie not sent)
✅ Step 3: Click "Strict" link to head back to https://samesite-strict.badsite.io/ (cookie not sent)
❌ Step 4: Refresh the page (cookie not sent)

In Chrome, refreshing the page will send the SameSite=Strict cookie, whereas it does not in Firefox.
Christoph, can you take a look?
Flags: needinfo?(ckerschb)
(In reply to Valentin Gosu [:valentin] from comment #1)
> Christoph, can you take a look?

Pulling this over into DOM:Security. From a first glance it seems to me that Firefox implementation is more correct, but then again that's debatable.

Mark, what's your take on this one?
Component: Networking: Cookies → DOM: Security
Flags: needinfo?(ckerschb) → needinfo?(mgoodwin)
(In reply to Christoph Kerschbaumer [:ckerschb] from comment #2)
> (In reply to Valentin Gosu [:valentin] from comment #1)
> > Christoph, can you take a look?
> 
> Pulling this over into DOM:Security. From a first glance it seems to me that
> Firefox implementation is more correct, but then again that's debatable.

If the user refreshes, I think we can assume that they mean to visit this as a first-party website.
However, if the website refreshes with location.reload() then I think we shouldn't.

Also, do we change |window.opener| on refresh?
(In reply to Christoph Kerschbaumer [:ckerschb] from comment #2)
> Mark, what's your take on this one?

There are cases where refreshing a page following some cross-origin navigation could be dangerous; consider a form POST from a document at https://samesite.badsite.cross-origin.io/ to https://samesite-strict.badsite.io/ - the user 
*might* be OK with reloading the page but they have no way of knowing what they're POSTing (IIRC firefox will ask if you want to resubmit, but the effect of resubmitting *with* cookies is different and we shouldn't expect users to understand this difference).

As Freddy mentions; we certainly shouldn't send in the case of location.reload().

As April points out, the spec isn't clear; given that, I'm happy with the behaviour Firefox has.
Flags: needinfo?(mgoodwin)
Mark, in that case do you wanna file a bug against the spec?
Flags: needinfo?(mgoodwin)
Priority: -- → P3
Whiteboard: [domsecurity-backlog1]
Let me know if you'd like me to rig up some test cases on badsite.io for things that might be dangerous.

I was talking to some Google folks (specifically Chris and Emily) yesterday about SameSite cookies, and they felt that refreshes should send the cookies. As you both point out it's actually a lot more complicated than a binary yes/no on whether to send them, and would probably be good to get written down in the spec.
(In reply to April King [:April] from comment #6)
> Let me know if you'd like me to rig up some test cases on badsite.io for
> things that might be dangerous.

Yes, please. Better still, we *might* be able to do some of these things with WPT - it might be worth investigating whether we can use the WPT testdriver functionality to accurately simulate some of these things (I had a brief conversation with jgraham about this possibility but I've not followed up with actual code).

> I was talking to some Google folks (specifically Chris and Emily) yesterday
> about SameSite cookies, and they felt that refreshes should send the
> cookies. As you both point out it's actually a lot more complicated than a
> binary yes/no on whether to send them, and would probably be good to get
> written down in the spec.

Agreed.

(In reply to Christoph Kerschbaumer [:ckerschb] from comment #5)
> Mark, in that case do you wanna file a bug against the spec?

I can do; not clearing my ni? because I don't think I'm going to get time to address this before PTO next week.
(In reply to Mark Goodwin [:mgoodwin] from comment #7)
> (In reply to April King [:April] from comment #6)
> > Let me know if you'd like me to rig up some test cases on badsite.io for
> > things that might be dangerous.
> 
> Yes, please. Better still, we *might* be able to do some of these things
> with WPT - it might be worth investigating whether we can use the WPT
> testdriver functionality to accurately simulate some of these things (I had
> a brief conversation with jgraham about this possibility but I've not
> followed up with actual code).

Assuming we agree on a clarification, are you interested in doing something like this?
Flags: needinfo?(april)
I've never written any WPTs, but I could certainly try. Rigging up something on badsite.io shouldn't be a problem at all.
Flags: needinfo?(april)

This behavior or bug (since it seems debated what it is) prevent a workaround for the following bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1599368.

If the user refreshes, I think we can assume that they mean to visit this as a first-party website.
However, if the website refreshes with location.reload() then I think we shouldn't.

I do not think a refresh initiated by a page itself should be considered cross-origin. That is no code coming from another website that initiates that additional navigation (unless the page suffers from some XSS). Treating it as cross-origin looks to me as over-interpreting what SameSite is meant for.
Of course a site doing a page refresh by itself could then enable CSRF attacks if doing it on a sensitive page, and if the browser does not behave as Firefox currently does. (So, if it does send strict cookies on refresh of a page initially loaded cross-origin). But I think that is the web site developer responsibility to be aware of the risk.
In my case, I would enable the refresh only on read-only pages. Anyway not having the cookie from the cross-site navigation already cause sensitive url to be redirected on non-sensitive url in my site case. I know some other site may instead just reply directly a 403/401 while staying on the url, causing a refresh to be potentially dangerous. But once again, that is the job of the site developers to be aware of this. If they want to be properly protected from CSRF attack only through SameSite Strict cookies, they just cannot leave the url unchanged when required cookies are missing.

Additional note on this bug/behavior: hitting F5 does exhibit it, but refreshing the page by hitting enter from the location (URL) bar does not: strict cookies are then sent, even if the page was loaded from a cross site link. From my viewpoint, that is just another way of refreshing a page. If it is wanted to not send Strict Cookies when refreshing a cross-site loaded page, then hitting the enter key without changing the url in the location bar should not send them either.

This was explored a bit further by Chrome folks here: https://github.com/httpwg/http-extensions/issues/628#issuecomment-754845672, and they've proposed new spec language here: https://github.com/httpwg/http-extensions/pull/1384.

Testing Firefox 87 manually I see the following:

  • Reloads triggered by browser UI (i.e., refresh button, shift+refresh button, control+r, f5) all preserve the prior same-site state meaning if the initial load was cross-site then all of the refreshes are as well
  • location.reload() will preserve the prior same-site state
  • meta refresh will switch from cross-site to same-site
  • window.location = window.location; will switch from cross-site to same-site

So we're certainly inconsistent at the moment. I suggest we align with the proposed spec language from the Chrome folks. Namely that we want to preserve the same site status on reloads triggered by a user, but allow reloads triggered by a site to become samesite. That would mean treating loads that result from location.reload() as samesite.

The alternate means locking down all self-navigations a page can perform, which seems difficult in practice. An attacker would need first-party JS access to trigger such a self-navigation, and at that point their JS also has the ability to read out non-HTTPOnly samesite=strict cookies / trigger embedded requests to any samesite endpoint with samesite=strict cookies. And even if we locked down all self-navigations, that JS could hook into the very first user click (anywhere on the page) and use it to navigate.

Flags: needinfo?(fbraun)
Flags: needinfo?(ckerschb)
Flags: needinfo?(amarchesini)

(In reply to Steven Englehardt [:englehardt] from comment #12)

So we're certainly inconsistent at the moment. I suggest we align with the proposed spec language from the Chrome folks. Namely that we want to preserve the same site status on reloads triggered by a user, but allow reloads triggered by a site to become samesite. That would mean treating loads that result from location.reload() as samesite.

I agree that consistency is key - I am fine with updating our implementation to match the proposed algorithm. Ideally we have web-platform tests for all the scenarios mentioned above. This way we not only agree in the spec but also ensure we are all actually follow the spec.

Flags: needinfo?(ckerschb)
Flags: needinfo?(fbraun)

Hi everybody, we stumbled upon this behaviour while trying to understand the reason the login window of OpenMediaVault project didn't work sometime (landing from a different origin, we discovered). Please see the detailed analysis at the bottom of this issue: https://github.com/openmediavault/openmediavault/issues/926#issuecomment-761558724

The login procedure uses an XHR POST to login and receives a "logged in" SameSite=strict cookie in the response, that is stored but not passed back at the next refresh of the page (even when opening the login page coming from a different origin). The subsequent refresh doesn't contain the cookie because of what discussed so far.

Note that the XHR seems to pass (previously-set) SameSite=strict cookies, so it seems XHR doesn't follow the same rule of refresh / F5. It seems that the browser is in "same-site" mode for XHR, but not for the refresh. Please keep XHR into account in this discussion.

Thanks.

Flags: needinfo?(amarchesini)
Summary: SameSite=Strict cookies aren't sent upon page refresh → Treat loads the result from location.reload() as samesite
Depends on: 1745946
Severity: normal → S3

(In reply to Steven Englehardt [:englehardt] from comment #12)

[...]
Testing Firefox 87 manually I see the following:

  • Reloads triggered by browser UI (i.e., refresh button, shift+refresh button, control+r, f5) all preserve the prior same-site state meaning if the initial load was cross-site then all of the refreshes are as well
  • location.reload() will preserve the prior same-site state
  • meta refresh will switch from cross-site to same-site
  • window.location = window.location; will switch from cross-site to same-site

[...]

The methods meta refresh and window.location = window.location behave identically when it comes to switching from cross-site to same-site (in Firefox). But in the Chrome browser, they differ when it comes to caching. A refresh via window.location = window.location caches the response. The next refresh with window.location = window.location then loads the response from the cache. With meta refresh, however, the response is neither cached nor loaded from the cache.

This difference was relevant for me when solving a specific problem. So I like to add this here.

I'm testing wich Chrome in version 120.0.6099.225.

Btw. The behavior described by [:englehardt] still applies to Firefox in version 115.7.0esr.

You need to log in before you can comment on or make changes to this bug.