3 ms·
Lots of stuff about this article is strange: * The author first describes a mildly convoluted cookie-stripping middleware under "Actually Solving The Problem"
by alexpetros 2y ago
Lots of stuff about this article is strange:
* The author first describes a mildly convoluted cookie-stripping middleware under "Actually Solving The Problem" and includes the much simpler `SameSite=Lax` cookie under "Defense in Depth" later on.
* The author neglects to mention that `SameSite=Lax` is the default on all major browsers (still good to set it, though), which basically blows up the premise of the article, that all your cookies are one cross-origin form away from being mis-used
* The author recommends _loosening_ the default CORS protections, which I guess makes sense if you think "CORS Is Stupid", but definitely makes your server less secure for basically no reason.
The best way to deal with the Cross-Origin Resource Sharing restrictions is to simply set up your site so that it doesn't need to make cross-origin requests: serve your site from the same domain that it makes requests to, and make sure you set your cookies with `SameSite=Lax`, `Secure`, and `HttpOnly`. I outline this approach in more detail here: https://htmx.org/essays/web-security-basics-with-htmx/ https://htmx.org/essays/web-security-basics-with-htmx/
Also, while I still think that existing browser controls basically solve this problem, it's true that POST forms have some sneaky vulnerabilities due to backwards compatibility concerns. One way to help fix that is by introducing PUT, PATCH, and DELETE to HTML forms—methods which don't have the same CORS exceptions: https://alexanderpetros.com/triptych/form-http-methods https://alexanderpetros.com/triptych/form-http-methods
- noduerme 2y agoFTA: >> The default policy allows making requests, but you can’t read the results. This seems like the central premise of the article. Is the author just wrong about this? I'm confused. As I understand it, if the remote site sets Access-Control-Allow-Origin to a wildcard, and doesn't explicitly name the origin, then by default the browser will not send credentials with a request. Isn't that CORS working as intended? Was there some time in the past when browsers defaulted cookies to `SameSite=None`?
- kevincox 2y agoHow can the browser know not to send the credential before it gets the response that contains the Access-Control-Allow-Origin header? This is the crux of the issue. For many types of request (notably with `Content-Type: application/json`) the browser will send a preflight request. But there is a carve-out for "simple" requests which includes some POST requests. https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simpl... If you can be sure that you application doesn't do any writes on "simple requests" then your job gets a lot easier and you can mostly rely on `Access-Control-Allow-Origin: *` omitting credentials. But if you have endpoints that accept form-posts or don't check the request's Content-Type header than you need to be extra careful.
- kevincox 2y agoThis is a good point, I tried to research `SameSite=Lax` but most of the docs don't actually mention how it applies to form posts. But the actual RFC does specify that it only applies to top-level "safe" methods. So form POSTs are actually protected by default. https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-same-site-00#section-4.1.1 https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-coo... This does actually make it pretty robust, although it does become an issue if you actually want to support cross-site form posts. But that is a pretty niche use case. > includes the much simpler `SameSite=Lax` cookie under "Defense in Depth" later on. The reason that I put the cookie-ignoring solution first is that it is robust and flexible. If you do want to support some cross-domain interaction with a logged in user you need something other than `SameSite=Lax`. The "ignore auth unless explicitly handled" provides a simple and effective baseline while being much more flexible. I also don't think ignoring cookies across you app is any more "mildly convoluted" that ensure that all cookies have a specific set of attributes. Both basically require a common layer to implement, but it seems more likely that someone adds a cookie somewhere without the approved helper than that they somehow accept a request that bypasses the middleware. > `SameSite=Lax` is the default on all major browsers It isn't. For some reason Firefox has had it prefed off since forever: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#browser_compatibility https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Se... But it is well supported if you set it explicitly. > loosening the default CORS protections ... but definitely makes your server less secure for basically no reason. Can you elaborate? The "loosening" described is equivalent to what can be accomplished via a CORS proxy (other than possible network perspective concerns if they apply to your use case). So it isn't really loosening it. How does this make your server less secure? This is explained here: https://kevincox.ca/2024/08/24/cors/#shouldnt-i-be-more-specific https://kevincox.ca/2024/08/24/cors/#shouldnt-i-be-more-spec... > One way to help fix that is by introducing PUT, PATCH, and DELETE to HTML forms—methods which don't have the same CORS exceptions This would be nice, but it seems to mostly be a workaround, you can choose a different verb just to get CORS protection but now you can't use POST even if it is appropriate.
- alexpetros 2y agoFirst off, really appreciate you engaging with the feedback so thoughtfully! > This does actually make it pretty robust, although it does become an issue if you actually want to support cross-site form posts. But that is a pretty niche use case. In my opinion, the dominant security message for web applications should be: unless you are a specialist you should not be supporting cross-domain credentialed interactions. It's a simple rule that lets the browser do most of the security work for you. The premise of your article that I most disagree with is "the automatically provided cross-origin protections are completely broken. Every single site that uses cookies needs to explicitly handle it." That's a scary line and it's simply not true for single-origin sites which is a) most sites and b) what should be encouraged. > The reason that I put the cookie-ignoring solution first is that it is robust and flexible. The robustness and flexibility of the solution are also add to the complexity of it (I did say "mildly"). Are the couple lines of python terribly complex? No. But middleware is a relatively big jump in complexity from just setting the `SameSite` property. If you said "in the situations where you need to support cross-origin requests, you'll have to do some extra work here" I'd have no problem with it. > I also don't think ignoring cookies across you app is any more "mildly convoluted" that ensure that all cookies have a specific set of attributes. Agree to disagree here, I guess, but setting cookie properties definitely easier (it's one line!) than filtering all your request cookies at runtime. The latter also involves a bunch of unnecessary computation. > Can you elaborate? The "loosening" described is equivalent to what can be accomplished via a CORS proxy (other than possible network perspective concerns if they apply to your use case). So it isn't really loosening it. How does this make your server less secure? Well, you're setting a header that says to allow a bunch of stuff that the browser would otherwise block (for instance, cross-origin PUT and DELETE methods). The point of CORS isn't that bad actors can't proxy it—you can always just send a curl—it's to make the median web client (i.e. your less tech-savy relative) more secure. In effect, you're disabling a chunk of CORS because it's annoying and replacing it with your own security mechanism. I don't think most people should do that, and there's better, easier advice we can give them. > This would be nice, but it seems to mostly be a workaround, you can choose a different verb just to get CORS protection but now you can't use POST even if it is appropriate. True, I'd also like to see them add a CREATE method with the same semantics as POST but with with CORS protections. But there's only so much you can accomplish in one proposal :P