OWASP / CheatSheetSeries

The OWASP Cheat Sheet Series was created to provide a concise collection of high value information on specific application security topics.
https://cheatsheetseries.owasp.org
Creative Commons Attribution Share Alike 4.0 International
28.14k stars 3.95k forks source link

New Security with CORS CS #875

Open jamesarosen opened 2 years ago

jamesarosen commented 2 years ago

What is missing or needs to be updated?

The HTML5 security cheat sheet currently says

Keep in mind that CORS does not prevent the requested data from going to an unauthorized location. It's still important for the server to perform usual CSRF prevention.

It is absolutely true that CSRF protection is required for CORS, but it's a little imprecise. Specifically, the Cross-Site Request Forgery Prevention Cheat Sheet lists the double-submit cookie as the first line of defense, but that pattern is not possible in a CORS environment. (Unless the Origin and request domains happen to be subdomains of a shared parent on which the cookie could be set.)

How should this be resolved?

I suggest clarifying -- in either or both of the cheat sheets -- which CSRF protection mechanisms are best suited to CORS environments.

MixMax's Using CORS policies to implement CSRF protection has some additional information on using Origin verification to protect against CSRF attacks on CORS requests.

jmanico commented 2 years ago

Agree 100%, Would you like to suggest specific PR's? I'm with you.

jamesarosen commented 2 years ago

Would you like to suggest specific PR's?

I'm not sure I'm qualified. I'm happy to contribute, but I'd want there to be some careful review.

mackowski commented 2 years ago

@jamesarosen do not worry we will help you ;-)

jamesarosen commented 2 years ago

Some highlights:

And something I'm unsure about:

What should the server do when the Origin is not present? Should it use Referer? Block the request? What about if it has to support non-browser clients that don't supply an Origin header?

jamesarosen commented 2 years ago

Also: Chrome currently allows CORS requests on localhost (e.g. from http://localhost:3000 to http://localhost:3001) without SameSite=None; secure, but that's going to change soon. That means that using CORS on localhost will require https:.

jmanico commented 2 years ago
  • The double-submit cookie pattern is not possible when making cross-origin requests

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials ?

jamesarosen commented 2 years ago

withCredentials is certainly necessary for submitting cookies and that's something I'll add to the "if you're using cookies for authentication" section.

But if you were implying that withCredentials could solve the double-submit cookie problem… I don't think so. The problem isn't submitting the token. The problem is getting it.

In a non-CORS scenario, the scheme looks something like this:

  1. browser makes request to GET https://www.example.com
  2. server generates a random CSRF token and returns it in two different ways simultaneously:
    1. Via a Set-Cookie header with HttpOnly; domain=www.example.com; path=/. The browser will resubmit this cookie on subsequent requests.
    2. Via a <meta> tag in the body. The application uses some JavaScript to read the token from the tag and attach it to any XHR or Form submission requests.

But in a CORS scenario, no server can do both (2.1) and (2.2) in the same request.

  1. browser makes request to GET https://www.example.com
  2. server generates a random CSRF token and returns it in the <meta> tag
  3. browser makes XHR CORS request to POST https://api.example.com/sessions; there's no cookie on api.example.com to "double" submit

You could try to solve this by first making a request to api.example.com to establish the cookie and fetch the value. But one of two things is true:

  1. The request includes the set-cookie header and an empty body. The browser will automatically resubmit this cookie, but the JavaScript can't read the value from the cookie because of the origin security policy.
  2. The request includes the set-cookie header and the same value in the body. I believe this defeats the purpose of the security token. In other words, an attacker could make the user make the request then read the value out.
jamesarosen commented 2 years ago

Since Google and Apple intend to block all third-party cookies (by default at least), it's probably not wise to recommend authenticating CORS requests with cookies on the host domain. That leaves two options:

  1. When the host and origin domains share a parent domain (e.g. www.example.com and api.example.com), a cookie set on the parent domain (e.g. example.com). This is not considered a best practice because a vulnerability in another application running under a different subdomain (e.g. test.example.com) could become a vulnerability in the primary applications.
  2. Use something other than cookies. For example, store a JWT in LocalStorage and pass it via the Authentication header. This is not considered a best practice because LocalStorage doesn't have the same protections that an HttpOnly Cookie does. For example, an XSS vulnerability would make it easy for an attacker to read and exfiltrate the JWT session tokens.

This suggests a need for a richer "Security with CORS" document.

mackowski commented 2 years ago

I agree, it looks like a new cheatsheet that can be referenced from CSRF and HTML5