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

Meta referrer="no-referrer" causes all posts to fail in Chrome #28299

Closed
sidot3291 opened this issue Mar 5, 2017 · 12 comments
Closed

Meta referrer="no-referrer" causes all posts to fail in Chrome #28299

sidot3291 opened this issue Mar 5, 2017 · 12 comments

Comments

@sidot3291
Copy link

sidot3291 commented Mar 5, 2017

Steps to reproduce

Add this to your <head>:
<meta name="referrer" content="no-referrer">

Create a form with method='post' that points to a route on the same origin, go to the form in Google Chrome and click submit.

Expected behavior

I'm reporting this as an issue instead of a PR because I'm not quite sure of the expected behavior.

The request is failing because Chrome sets the Origin header to the string "null"

My instinct is that the request should succeed, since the valid_request_origin? method allows for the Origin header to be missing altogether:

      def valid_request_origin? # :doc:
        if forgery_protection_origin_check
          # We accept blank origin headers because some user agents don't send it.
          request.origin.nil? || request.origin == request.base_url
        else
          true
        end
      end

However, I'm not an expert in CSRF and recognize the proper solution might be not to utilize this meta tag in the <head>

Actual behavior

The request fails during the valid_request_origin? check

System configuration

Rails version: 5.0.2

sidot3291 added a commit to sidot3291/rails that referenced this issue Mar 6, 2017
Fixes rails#28299

Since Firefox doesn't send the Origin header when the meta tag is set but Chrome sends it with a "null" string, this change ensures Rails behaves consistently across browsers.
@pixeltrix
Copy link
Contributor

Why would you set no-referrer on a page you were POST-ing back to yourself? Surely using same-origin would be a better choice? If the page with no-referrer on isn't under your control then isn't that the correct behavior?

@sidot3291
Copy link
Author

For reference:
https://w3c.github.io/webappsec-referrer-policy/

It looks like same-origin would solve my problem while also preventing the referrer from being sent cross-origin, which is my goal. Thanks for that idea.

That said, if I must set to same-origin, why does an omitted Origin header also pass the valid_request_origin? test? Is there a scenario where that exception is desired, and if so, are we confident that Chrome omits the header instead of sending through a "null" string?

Code looks like it was introduced here:
8578353

@pixeltrix
Copy link
Contributor

why does an omitted Origin header also pass the valid_request_origin? test?

Don't know for sure but some scenarios could be posting from HTTP to HTTPS and legacy browsers.

I'm just loathe to special case values because the current version of a particular browser has a bug or behaves inconsistently - those kind of things tend to hang around in a framework for years (see #18255).

@sidot3291
Copy link
Author

This has turned into a rabbit hole for me, and I think I'm going to set forgery_protection_origin_check to false.

It turns out the Chrome has not implemented same-origin, and I don't see another option to prevent referrer/origin information from being sent externally.

There is a lot of inconsistency across browsers in their current state on macOS:

Chrome POST to same origin:
default: sends Origin according to spec
no-referrer set: sends Origin: "null"

Firefox POST to same origin:
default: does not send Origin
no-referrer set: does not send Origin

Safari POST to same origin:
default: sends Origin according to spec
no-referrer set: sends Origin same as default case

I see an argument that we don't want to allow "null" since setting "no-referrer" would then represent a reasonable approach for attackers to undermine this check for cross-origin requests.

However, I also worry that working with Origin is like trying to prevent CSRF with the Referer header all over again. Tokens still feel like the gold standard, and with those still in use, I'm not sure which attack vector the forgery_protection_origin_check realistically protects against, especially given such inconsistency across browsers.

@sgrif
Copy link
Contributor

sgrif commented Oct 9, 2017

So after doing further digging, this is not a bug. It could be considered a bug in Firefox that it isn't sending this value, (see https://bugzilla.mozilla.org/show_bug.cgi?id=446344), but basically the reason this is happening is due to a rather vague line in the Origin spec:

Whenever a user agent issues an HTTP request from a "privacy-sensitive" context, the user agent MUST send the value "null" in the Origin header field.

It's likely that most people setting the no-referrer value probably want strict-same-origin instead. If they don't, they'll need to change the value of the forgery_protection_origin_check to false and opt out of this. Rails is currently doing the right thing as described in the original proposal of the Origin header for CSRF protection

If the Origin header is present, the server must reject any requests whose Origin header contains an undesired value (including null).

@sgrif sgrif closed this as completed Oct 9, 2017
@phillycheeze
Copy link

@sgrif I think there should be more discussion on this issue. As the post you provided, there are two conflicting entries in the specs.

As it stands right now, the two specs have a conflict. So the solution is that I cannot set that header? Browsers are correctly adhering to spec by setting it to "null" (which is the newer spec from what I am seeing).

The second spec you linked is a proposal from Stanford. Did this make it into an actual specification? If so, can you link it? Even then, we should adopt to the newer spec and simply whitelist the "null" value. Or at the very least, continue the debate. As it stands, Rails simply does not work with a valid header value.

@sgrif
Copy link
Contributor

sgrif commented Oct 9, 2017

The HTTP specification of the request header does not specify server behavior, because it's not relevant to the intent. The paper I linked to is the intent behind the header being specified. There's no "newer" or "older" conflict here.

@phillycheeze
Copy link

The privacy-sensitive context in the specification also goes on to say that the interpretation is up to the application/browser. So null string values could get added later on for other header values (or any other changes that we can't predict).

I read the propsal and understand why the Origin header exists. But the proposal says null values meaning true null, not the string null. It says to prevent non-support browsers from being accepted, which won't happen in this string approach.

@sgrif
Copy link
Contributor

sgrif commented Oct 9, 2017

But the proposal says null values meaning true null, not the string null

There is no difference in HTTP. The only values are strings. You cannot set a "true null" for a header

@phillycheeze
Copy link

You're correct. My apologies.

There is still a gap here. As the header gains more attention and privacy concerns increase, this will only be an increasing challenge, correct?

@sgrif
Copy link
Contributor

sgrif commented Oct 9, 2017

It will, which is why on the corresponding PR I asked that we instead raise an error message that specifically explains the issue to the person receiving the error. I suspect the vast majority of people using no-referrer would be fine with strict-same-origin. Those that aren't will need to opt out of origin checking for CSRF. We can teach that in the error message, but after spending a hefty chunk of the day researching this and discussing it with people involved, I'm convinced that the current behavior of Rails is the correct one.

@phillycheeze
Copy link

Yes, I definitely agree with you. Thanks for all of your help and dedicating time to this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants