0

in the AMP Docs, the following snippet is given:

If the Origin header is set:

  1. If the origin does not match one of the following values, stop and return an error response:

    • <publisher's domain>.cdn.ampproject.org

    • the publisher's origin (aka yours)

      where * represents a wildcard match, and not an actual asterisk ( * ).

  2. Otherwise, process the request.

If the Origin header is NOT set:

  1. Verify that the request contains the AMP-Same-Origin: true header. If the request does not contain this header, stop and return an error response.
  2. Otherwise, process the request.

What I don't understand is how the AMP-Same-Origin header provides a form of security.

TLDR:

Couldn't anyone provide an AMP-Same-Origin: true header in a browser missing the Origin header and skip CSRF protection even if it's not on a trusted AMP CDN?

2 Answers2

1

It adds security for "simple" CORS requests that would otherwise not require an OPTIONS intermediate request. By requiring the AMP-Same-Origin when the Origin header is not set, you can fail all requests that would otherwise cause side effects, even if they weren't viewable at the endpoint, because they would not fail until they came back to the browser


It is not possible to add the AMP-Same-Origin header on a cross origin request if CORS isn't explicitly allowed. Thus, you do not need to add a random string like you usually would with a csrf token. Check this related question about it

Simply checking the value is sufficient at the moment, but future technologies and attacks may be leveraged to break your protection.

Simply checking for a custom header is sufficient for now. However, this is not the best practice and for additional defense in depth it is normally recommanded to have additional security in depth as per MDN

To prevent cross-origin writes, check an unguessable token in the request — known as a Cross-Site Request Forgery (CSRF) token. You must prevent cross-origin reads of pages that require this token.

You could also ask yourself (or not if you're already aware of it), why don't they just set the Origin header instead of setting an additional header ? Well, the Origin header is a forbidden header and cannot be set programmatically (MDN).

Xavier59
  • 2,924
  • 4
  • 18
  • 34
  • "Well, the Origin header is a forbidden header and cannot be set programmatically (MDN)." That's exactly the point of my question. If you're checking for a header that can be set programmatically, how does that even remotely change anything? the attacker can just set the AMP-Same-Origin header on a request using a browser they know doesn't sent the Origin header. At the least, the docs saying to respond with the source origin if AMP-Same-Origin is there, but I don't see the point of even checking a header that anyone can add just to respond with same-origin as the response anyways – Sampson Crowley Aug 24 '20 at 22:35
  • The reason it still provides protection is that the servers are told to only respond to same-origin requests if given AMP-Same-Origin and no Origin is given; so I don't see what the point is of even checking for the header instead of just responding to the request with your origin set as the "allow" value and letting it fail on its own; since the requests will fail CORS checks anyways if it's not from the same origin – Sampson Crowley Aug 24 '20 at 22:59
  • is it to guarantee that the request is an XHR request? – Sampson Crowley Aug 24 '20 at 23:12
  • If you are on evil.com and make a xhr request on *.cdn.ampproject.org and considering that ampproject.org doesn't have a CORS policy (aka Access-Control-Allow-Origin: * and Access-Control-Allow-Headers: AMP-Same-Origin), then you cannot set the AMP-Same-Origin header. The browser will forbid it when you will try to send the request. – Xavier59 Aug 25 '20 at 10:06
  • You cannot send a xhr request at all if it is not the same origin and *.cdn.ampproject.org doesn't send back the header Access-Control-Allow-Origin: *. The only thing you can do is make a GET or POST request via an image or a form. But you cannot trick the browser into adding AMP-Same-Origin – Xavier59 Aug 25 '20 at 10:09
  • Access-Control-Allow-Origin is a response header, it has absolutely nothing to do with the CDN servers for the AMP project. the Origin header allows your server to respond to *.cdn.ampproject.org safely; the instructions for AMP-Same-Origin tell you only to respond to your own origin. So again, I don't see the point of looking for the header when you're not responding with Access-Control-Allow-Origin: *, you're responding with Access-Control-Allow-Origin: mydomain.com – Sampson Crowley Aug 25 '20 at 15:46
  • "If you're checking for a header that can be set programmatically, how does that even remotely change anything? ". You're assemption is wrong. You cannot set programmatically the header AMP-Same-Origin if AMP doesn't send you the response headers I mentionned. I am not sure to get what you fail to understand here and I'd like to help. Come over to chat https://chat.stackexchange.com/rooms/112273/csrf – Xavier59 Aug 25 '20 at 19:10
  • you seem to be confused about what the AMP project is. no one is making any requests to cdn.ampproject.org. cdn.ampproject.org is a CDN that serves static caches of pre-rendered pages for instant delivery on mobile devices. it is the Origin not the endpoint/source server – Sampson Crowley Aug 25 '20 at 20:51
0

Couldn't anyone provide an AMP-Same-Origin: true header in a browser missing the Origin header and skip CSRF protection even if it's not on a trusted AMP CDN?

No, because if the request is cross-origin and sent with a custom header, it must send the Origin header.

This answer explains when the Origin header must be sent, and how the spec changed in 2016-12-09 and now all POST requests must send Origin.

But even before that, it notes that:

  • previously no Origin was sent for a same-origin POST
  • previously no Origin was sent for cross-origin POST from a (without CORS)

Therefore, even before 2016-12-09, a POST request without Origin and with a custom header set could only have been sent from the same origin.

A fetch request with method POST can be sent with mode no-cors, but then it won't send custom headers.