29 ms·
Why do we have both CSRF protection and CORS?
- deleted 2y ago[deleted]
- IgorPartola 2y agoWhat I never quite grasped despite working with HTTP for decades now: how come before CORS was a thing that you could send a request to any arbitrary endpoint that isn’t the page origin just not be able to see the response. Was this an accidental thing that made it into the spec? Was this done on purpose in anticipation of XSS-I-mean-mashups-I-mean-web-apps? Was it just what the dominant browser did and others just followed suit?
- PantaloonFlames 2y agoI don’t have a Time Machine or a 1998-era browser but I’m not sure what you described was the case. I think in the before times, a browser could send a request to any arbitrary endpoint that was not the page origin, and it could also see the response. I might be wrong. But anyway, ancient history.
- deleted 2y ago[deleted]
- fweimer 2y agoWikipedia references this part of the Netscape Navigator documentation: https://web.archive.org/web/20020808153106/http://wp.netscape.com:80/eng/mozilla/3.0/handbook/javascript/advtopic.htm#1009533 https://web.archive.org/web/20020808153106/http://wp.netscap... This suggests that the same-origin policy was introduced rather quickly after the introduction of Javascript, as a security fix.
- LegionMammal978 2y agoYou still can make many kinds of requests [0] to an arbitrary endpoint that isn't the page origin, without being able to see the response. (Basically, anything that a <link> or a form submission could do.) And you can't include any cookies or other credentials in the request unless they have SameSite=None (except on ancient browsers), and if you do, then you still can't see the response unless the endpoint opts in. Really, there's exactly one thing that the mandatory CORS headers protect against: endpoints that authorize the request based on the requester's IP address and nothing else. (The biggest case of this would be local addresses in the requester's network, but they've been planning on adding even more mandatory headers for that [1].) They don't protect against data exfiltration, third-party cookie exfiltration (that's what the SameSite directive is for), or any other such attack vector. [0] https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simpl... [1] https://wicg.github.io/private-network-access/ https://wicg.github.io/private-network-access/
- IgorPartola 2y agoYes I know this is still the case today. My question is: how did this come about? It seems to me that in the olden days the idea of cross origin requests wasn’t really needed as you’d be lucky to have your own domain name, let alone make requests to separate services in an era of static HTML pages with no CSS or JavaScript. What exactly was this feature for? Or was it not a feature and just an oversight of the security model that got codified into the spec?
- LegionMammal978 2y agoHotlinking <img>s from other domains has been a thing forever, as far as I'm aware, and that's the archetypical example of a cross-origin request. <iframe>s (or rather, <frame>s) are another old example. And it's not like those would've been considered a security issue, since at worst it would eat up the other domain's bandwidth. The current status quo is a restriction on what scripts are allowed to do, compared to those elements.
- IgorPartola 2y agoBy definition those do allow you to see the response so that’s not really what is being discussed.
- deleted 2y ago[deleted]
- Muromec 2y agoNot really. You as an application generally can't read across origin boundaries, but you can ask browser to show it to the user.
- deleted 2y ago[deleted]
- hinkley 2y agoThe field of web applications didn’t really blow open until we were into the DotCom era. By then Berners-Lee’s concept for the web was already almost ten years old. I think it’s hard for people to conceive today what it was like to have to own a bookshelf of books in order to be a productive programmer on Windows, for instance. Programming paradigms were measured literally in shelf-feet. Practically part of the reason Java took off was it was birthed onto the Internet and came with Javadoc. And even then the spec for Java Server Pages was so “late” to the party that I had already worked on my first web framework when the draft came out, for a company that was already on its second templating engine. Which put me in rarified air that I did not appreciate at the time. It was the Wild West and not in the gunslinger sense, but in the “one pair of wire cutters could isolate an entire community” sense.
- deleted 2y ago[deleted]
- Muromec 2y agoThat makes perfect sense in the early model of internet where everything was just links and documents. You can make an HTML form with action attribute pointing to a different domain. That's a feature, not a bug and isn't a security vulnerability in itself. Common use for this is to make "search this site in google" widgets. Then you can make the form make post requests by changing a method. Nothing wrong with this either -- the browser will navigate there and serve the page to user, not to the origin server. What makes it problematic is the combination of cookies from the destination domain and programmatic input or hidden field from the origin domain. But the only problem it can cause is the side-effects that POST request causes on the back end, as it again doesn't let the origin page to read the result (i.e. content doesn't cross the domain boundary). Now in the world on JS applications that make requests on behalf of the user without any input and backend servers acting on POST requests as user input, the previously ignored side-effects are the main use and are a much bigger problem.
- smagin 2y ago"search this site in google" shouldn't even be a POST request, but yeah, when we'll have better defaults for cookies it should work nicer. And if you are a web developer, you should check your session cookie attributes and explicitly set them to SameSite=Lax HttpOnly unless your frameworks does that already and unless you know what you're doing
- Muromec 2y agoIt was GET request, but the point is -- you can make the request from the browser to a different domain by making a form. With JS and DOM you can make a hidden iframe and make the request without user initiating it or noticing even, but in both cases you don't get to read the result.
- fweimer 2y agoI think it once was a common design pattern to have static HTML with a form that was submitted to a different server on a different domain, or at least a different protocol. For example, login forms served over HTTP were common, but the actual POST request was sent over HTTPS (which at least hid the username/password from passive observers). When Javascript added the capability to perform client-side form validation, it inherited this cross-domain POST capability. I don't know why <script> has the ability to perform cross-domain reads (the exception to the not-able-see-the-response rule). I doubt anyone had CDNs with popular Javascript on their minds when this was set in stone.
- Muromec 2y ago>I don't know why <script> has the ability to perform cross-domain reads That's because all scripts loaded on the page are operating in the same global namespace of the same javascript vm, which has origin of the page. Since there are no contexts granularity below the page level in VM, they have to either share it or not work. You can't read it however, you can ask browser to execute, the same way you can ask it to show an image. It's just execution can have result in a read as side-effect by calling a callback or setting well-know global variable
- fweimer 2y agoWhat I meant is that from a 1996 perspective, I don't see a good reason not to block cross-domain <script> loads. The risks must have already been obvious at the time (applications serving dynamically generated scripts that can execute out of origin and reveal otherwise inaccessible information). And the nascent web ad business had not yet adopted cross-domain script injection as the delivery method.
- Muromec 2y agoThere is no reason for browser to block them if the page is static and written by hand. If I added this script to my page, then I want to do the funny thing and if the script misbehaves, I remove it.
- Macha 2y agoI think you might have forgotten how immature the field of computer security, especially for network applications, was in the 90s/00s. This was the era of register_globals in PHP, CGI scripts written in C, HTTPS being some fancy luxury for banks and OS login dialogs you could kill with task manager.
- PantaloonFlames 2y agoTo me, this seems a better treatment of the topic: https://maddevs.io/blog/web-security-an-overview-of-sop-cors-and-csrf/ https://maddevs.io/blog/web-security-an-overview-of-sop-cors...
- Scaevolus 2y ago> JS-initiated requests are not allowed cross-site by default anyway Incorrect. You can use fetch() to initiate cross-site requests as long as you only use the allowed headers. https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_request_header https://developer.mozilla.org/en-US/docs/Glossary/CORS-safel...
- duskwuff 2y agoAnd JS can also indirectly initiate requests for resource or page fetches, e.g. by creating image tags or popup windows. It can't see the results directly, but it can make some inferences.
- 1oooqooq 2y agothere are so, so, so many ways to read this data back it's not even fun.
- Muromec 2y agoThere are ways, but they generally need a cooperation of both sides of the inter-domain boundary. What you generally can't do is make arbitrary reads from the context of other domain (e.g. call GET on their api and read a result) into your domain without them explicitly allowing it.
- duskwuff 2y agoRight. What you can sometimes do is observe the effects of the content being loaded, e.g. see the dimensions of an image element change when its content is loaded.
- RandomDistort 2y agoIs there some document somewhere that lists all the potential ways of doing stuff like this?
- TheRealPomax 2y agoAnd why do browsers not let users go "I don't care about what this server's headers say, you will do as I say because I clicked the little checkbox that says I know better than you". (On which note, no CSRF/CORS post is complete without talking about CSP, too)
- syntheticcdo 2y agoYou can! Go ahead and launch chrome with the --disable-web-security argument.
- TheRealPomax 2y agoThat's confusing the statement "I want the option to overrule it" with "I want it disabled". I don't, there's very good reason for them to always be on, but there has to be a way to tell it what I want my browser to do, rather than what a server owner, who spent all of zero minutes accepting the default helmet options, says what my browser should do. In the same way that a browser should let me say which off-site domains, specific scripts, and on-page elements should be blocked.
- siva7 2y agoAlmost as pointless as "Yes, accept all cookies" for our european friends.
- LegionMammal978 2y agoI'd think SameSite/Secure directives on cookies are genuinely important to avoid any malicious website from stealing all your credentials. Otherwise, I'd imagine it's the usual "Because those dastardly corporations will tell people to disable it, just because they can't get it to work!!!"
- tedunangst 2y agoBecause then you will get users whining I didn't know what the checkbox did and you shouldn't have let me check it.
- yoavm 2y ago
- mjevans 2y agoA post I was replying to got deleted, but I'd still like to gripe about the positives and negatives of the current 'web browser + security things' model. Better in the sense of not being locked into an outdated and possibly protocol insecure crypto-system model. Worse in the sense: Random code from the Internet shouldn't have any chance to touch user credentials. At most it should be able to introspect the status of authentication, list of privileges, and what the end user has told their browser to do with that and the webpage. If it weren't for E.G. IE6 and major companies allergic to things not invented there we'd have stronger security foundations but easier end user interfaces to manage them. IRL metaphors such as a key ring and use of a key (or figurative representations like a cash / credit card in a digital wallet) could be part of the user interface and provide context for _when_ to offer such tokens.
- hu3 2y ago> Random code from the Internet shouldn't have any chance to touch user credentials One thing that helps with this are HttpOnly cookies. "A cookie with the HttpOnly attribute can't be accessed by JavaScript, for example using Document.cookie; it can only be accessed when it reaches the server. Cookies that persist user sessions for example should have the HttpOnly attribute set — it would be really insecure to make them available to JavaScript. This precaution helps mitigate cross-site scripting (XSS) attacks." https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
- mjevans 2y agoThat does nothing to address Credentialing and Authorization issues.
- hu3 2y agoHow so? Cookies are the most common way to implement persistent user sessions and by consequence, authn/authz. If it can't be accessed by JS, then it at least does something, to say the least. Could you expand your reasoning?
- 2y ago
- matsemann 2y agoOne thing to note is that if you think you're safe from having to use csrf due to only serving endpoints you yourself consume by posting json, some libraries (like django rest framework) can also opaquely handle html forms if the content type header is set, accidentally opening you up for someone having a form on their site posting on users' behalf to yours.
- chuckadams 2y agoFirst thing I do on a Laravel site is add a middleware to all my API routes that allows only blessed content types (usually application/json and application/x-ndjson). With Symfony it's a couple lines of yaml.
- webdever 2y agosubdomains are not always the same origin. See "public suffix list". For an example think of abc.github.io vs def.github.io I didn't get the part at the end about trusting browsers. As a website owner you can't rely on browers as hackers don't have to use a browser to send requests and read responses
- hinkley 2y agoOr abc.wordpress.com Also a lot of university departments and divisions in large enough corporations need to be treated like separate entities.
- Muromec 2y agoYou do rely on browsers to isolate contexts. The problem with CSRF is that data leaks from one privileged context to another (think of reading from kernel memory of another vm on the same host on AWS). If you don't have the browser, you don't have the user session to abuse in the first place. The whole thing boils down to this: - browser has two tabs -- one with authenticated session to web banking, another with your bad app - you as a bad app can ask browser to make an http request to the bank API and the browser will not just happily do it, but also attach the cookie from the authenticated session the user has opened in the other tab. That's CSRF and it's not even a bug - you however can't as a bad app read the response unless the bank API tells browser you are allowed to, which is what CORS is for. maybe you have an integration with them or something Browser is holding both contexts and is there to enforce what data can cross the domain boundary. No browser, no problem
- beala 2y agoIf I may attempt to summarize: CORS is a mechanism for servers to explicitly tell browsers which cross-origin requests can read responses. By default, browsers block cross-origin scripts from reading responses. Unless explicitly permitted, the response cannot be read by the requesting domain. For example, a script on evil.com might send a request to bank.com/transactions to try and read the victim's transaction history. The browser allows the request to reach bank.com, but blocks evil.com from reading the response. CSRF protection prevents malicious cross-origin requests from performing unauthorized actions on behalf of an authenticated user. If a script on evil.com sends a request to perform actions on bank.com (e.g., transferring money by requesting bank.com/transfer?from=victim&to=hacker), the server-side CSRF protection at bank.com rejects it (likely because the request because it doesn’t contains a secret CSRF token). In other words, CSRF protection is about write protection, preventing unauthorized cross-origin actions, while CORS is about read protection, controlling who can read cross-origin responses.
- layer8 2y agoTo add to that: CORS is implemented by browsers based on standardized HTTP headers. It’s a web-standard browser-level mechanism. CSRF protection is implemented server-side (plus parts of the client-side code) based on tokens and/or custom headers. It’s an application-specific mechanism that the browser is agnostic about.
- femto113 2y agoSome additional color: CORS today is just an annoying artifact of a poorly conceived idea about domain names somehow being a meaningful security boundary. It never amounted to anything more than a server asking the client not to do something with no mechanism to force the client to comply and no direct way for the server to tell if the client is complying. It has never offered any security value, workarounds were developed before it even became a settled standard. It's so much more likely to prevent legitimate use than protect against illegitimate use that browsers typically include a way to turn it off. With CSRF the idea is that the server wants to be able verify that a request from a client is one it invited (most commonly that a POST comes from a form that it served in an earlier GET). It's entirely up to the server to design the mechanism for that, the client typically has no idea its happening (it's just feeding back to the server on a later request something it got from the server on a previous request). Also notable is despite the "cross-site" part of the name it doesn't really have any direct relationship to "sites" or domains, servers can and do use the exact same mechanisms to detect or prevent issues like accidentally submitting the same form twice.
- actinium226 2y agoThere's something that continues to confuse me about CSRF protection. What's to stop an attacker, let's call her Eve, from going to goodsite.com, getting a CSRF token, putting it on badsite.com, and duping Alice into submitting a request to goodsite.com from badsite.com?
- bastawhiz 2y agoThe csrf token is ideally tied to your session. If it's anonymous, Eve didn't need Alice to visit a page in the first place. If it's tied to a session, Eve can't create a working token for Alice.
- smagin 2y agoyup. You didn't imply it but just in case -- this token shouldn't be the same as session token. Session tokens should be `HttpOnly`, so that we don't even expose it to javascript
- blincoln 2y agoThe HttpOnly flag isn't really practical in modern web apps where so much logic runs in JS in the browser and makes requests to APIs. It's a leftover from an earlier era of web app architecture. If it can be enabled without breaking something, sure, its a good idea, but unless your app is 2000s-era ASP.NET code or CGI script, preventing browser-side JS from accessing the session token will probably break something.
- Macha 2y agoRight, but if you're doing a SPA, your SPA makes the login call and stores a copy of the session token in local storage, which unlike a cookie isn't automatically sent on any request, never mind cross-origin ones. Doesn't prevent against XSS of course, but then that's what CSP is for.
- bastawhiz 2y agoIt's only necessary to store the login token if your backend is on a different origin than your SPA is served from. It's not especially hard to avoid this.
- yonran 2y agoTo respond to a question in the blog post: >> The motivation is that the <form> element from HTML 4.0 (which predates cross-site fetch() and XMLHttpRequest) can submit simple requests to any origin,… > Question to readers: How is that in line with the SameSite initiative? I actually added that little paragraph to the MDN CORS article in 2022 (https://github.com/mdn/content/pull/20922 https://github.com/mdn/content/pull/20922) to clarify where the term “simple request” from CORS came from, since previously the article only said that it is not mentioned in the fetch spec. You’re right that the paragraph did not mention the 2019 CSRF prevention in browsers that support or default to SameSite=Lax (https://www.ietf.org/archive/id/draft-ietf-httpbis-rfc6265bis-19.html#strict-lax https://www.ietf.org/archive/id/draft-ietf-httpbis-rfc6265bi...), so cross-site forms with method=POST will not have cookies anymore unless the server created the cookie with SameSite=None. It is quite confusing that SameSite was added seemingly independently of CORS preflight. I wonder why browser makers didn’t just make all cross-origin POST requests require a preflight request instead of making same-site-flag a field of each cookie.
- shermantanktop 2y agoWhatever else these things do, one thing they don’t do is support easy diagnostic tracing when a legitimate use case isn’t quite configured properly. I have stared many times at opaque errors that suggested (to me) one possible cause when the truth ended up being totally different.
- ListeningPie 2y agoAt the bottom the article links to this discussion, but not your other articles. Did you happen to find the discussion on Hacker news or post it yourself? If you happened to have found it, has someone systematized linking their articles to their respective hacker news discussion?.
- smagin 2y agoThis is my blog. I haven't posted all the articles from there to hackernews. I think some of them are not worth discussing because too old or too poorly written, but some are just not posted because I didn't think of it at the time. Thanks for the interest, I will re-read what I wrote and post some.
- 0xbadcafebee 2y agoCan I get a flow chart please? This is complicated as hell. Alternately, can I get an entirely new application platform and set of standards? It would be nice to not carry 35 years of technical baggage into every application I want to make or use. Thanks.
- cies 2y agoCORS is flawed. The attacks you typically want to circumvent CORS for are so involved that setting up a little bitcoin-rented VPS as a MITM is not a big part of the hassle. browser ---> MITM VPS ---> real server The browser does some (pre-flight) check to see the the origin (`malicious.net`) is allowed to make requests. The real server would reject it. The MITM just rewrites the origin from `malicious.net` to `acceptable.org` which the real server does accept.
- smagin 2y agomitm means you can control users network, doesn't it? Another question, does this work with https? And the third one, if this was the thing some dishonest governments or vpn providers would do this already. Would be cool to read on that (genuinely, not implying this never happened)
- cies 2y ago> mitm means you can control users network, doesn't it? Not necessarily. Maybe this is not called MITM, but you have to put something in the middle :) > Another question, does this work with https? Sure. > And the third one, if this was the thing some dishonest governments or vpn providers would do this already. You are confused what this attack is about. Say I want to embed some widget on my website by which I can receive payments. I have to register my websites domain (technically protocol+domain(+port), aka origin) with the widget's provider. CORS is then used to make sure no-one can embed the widget, but those with registered origins. Only browsers are known to enforce CORS (do the checks AND provide the correct origin when doing the checks). Hence the MITM attack I propose works: the MITM does NOT give the correct origin to the real server.
- smagin 2y agoI asked that in the post but nobody answered still, let's try here. Why are CSRF tokens rotated? OWASP says it's somehow more secure but I don't really see why.
- rajnathani 2y ago> Browser is important > I want to emphasise how important browsers are in this whole security scheme. This is the important point which maybe many developers may not think about, that CORS is just a security "feature" within the browser. And, that if one made an HTTP(S) request directly to the server that they could simply spoof the origin. Obviously, there is reasoning to still have CORS for in order to protect users from malicious scripts on trusted websites or simply malicious websites.