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

What's the attack vector on /csrf? #6

Open
marfire opened this issue Jun 23, 2015 · 14 comments
Open

What's the attack vector on /csrf? #6

marfire opened this issue Jun 23, 2015 · 14 comments

Comments

@marfire
Copy link

marfire commented Jun 23, 2015

In a couple places this document emphasizes the following point: Make sure CSRF tokens can not be accessed with AJAX! Don't create a /csrf route just to grab a token.

I don't see the attack vector here. Such an endpoint would be no more or less secure than the usual practice of embedding the token in a hidden form field. In both cases the Same Origin policy will prevent a foreign script from reading the value.

That is: in both cases a foreign script can send a GET to that URL; in both cases the user's authentication cookie will be included, causing the server to return a valid CSRF token; and in both cases the browser's Same Origin policy will prevent the foreign script from reading the response to the GET, which keeps the token secure.

@marfire marfire changed the title What's the attack vector on /csrf? What's the attack vector on /csrf? Jun 23, 2015
@SEAPUNK
Copy link

SEAPUNK commented Sep 10, 2015

+1

I don't quite understand the reasoning behind that as well.

@frutality
Copy link

+1
That would be nice if @pillarjs can explain this point.

@bradfloodx
Copy link

+1

@jonathanong
Copy link
Member

if anyone wants to maintain this, i can add you as a collab. haven't had time to revise this yet.

@SEAPUNK
Copy link

SEAPUNK commented Nov 16, 2015

I'm not a security expert, but I can contribute to discussion, and help keep this repo alive if needed.

@dougwilson
Copy link

Best way to start is with PRs, of course :

@mikermcneil
Copy link

@marfire @jonathanong @bradleyflood @frutality @SEAPUNK when we decided to add csrf support to Sails core, we exposed a /csrfToken endpoint. Since then, I've spent a bunch of time thinking about this, particularly in the past month as @irlnathan and I have been working on the last chapter of the Sails book which covers security. I can say with a fair degree of confidence that this is safe as long as you follow some ground rules: the only time you need to worry about exposing a JSON endpoint which dispenses CSRF tokens is if that endpoint is accessible via a browser from an untrusted domain (CORS or JSONP). That's because, if it is accessible cross-domain, then e.g. if you visit http://hack-my-sauce.com in your browser, JavaScript running on that page can send a cross-domain request to the CSRF endpoint: http://your-normal-site-about-sauce.com/csrfToken and access it. Once javascript running on a nasty webpage has access to your CSRF token, even if all of your other endpoints do not allow cross-domain access, it's over (because it can use that csrf token to e.g. inject a hidden form pointed at POST http://your-normal-site-about-sauce.com/dollars/transfer-all-away with a hidden form field containing your CSRF token, then submit it as you! Now there's no way the naughty site could retrieve the response (which is the only reason why CSRF works anyway), but it could still circumvent CSRF protection and trigger actions.

Here's an attempt to summarize that word salad above into a more straightforward format:

  • CSRF protection is needed because visiting sites with all your browser cookies hanging out (even if they're HTTP-only) is like walking around the airport with a bullseye on your britches. It's possible (using dirty tricks) for unsavory sites you visit to use those cookies to send requests to other normal sites on behalf of you.
  • Fortunately, the unsavory characters can't receive the response unless you explicitly allow cross-domain access to the unsavory domain via CORS, or in general by implementing JSONP support for one of your endpoints.
  • But even without the response, they can still trigger actions as you (such as transferring all the money out of your bank account, or signing up for a mailing list about Uggs for men). So to protect against that, we can require all actions on a site to require a CSRF token-- a special token that our users can only get by sending a request to our site while logged in and parsing it out of the response.
  • Usually we inject CSRF tokens in HTML-- but the same principle applies if they're injected in a JSON response. The thing protecting your CSRF token is the browser's same-origin policy, not the fact that it's nestled in some HTML.
  • That said, there is a major caveat: if you expose your endpoint that returns the csrf token cross-domain via CORS (so it can be accessed via AJAX) or JSONP (so it can be accessed by including a script tag), then this is not safe. In Sails, if the csrfToken endpoint is exposed, we force it to never support JSONP or CORS (although note that technically CORS would be ok if you whitelisted domains you trusted-- again, the only danger comes from untrusted domains)

Happy to dig into this more if it would help anybody out (if I don't respond, just remind me on twitter).

@mikermcneil
Copy link

(One other major caveat is that nasty browser plugins and/or compromised native software running on a computer completely circumvent everything I discussed above)

@jroitgrund
Copy link

JSON hijacking is one of the possible attack vectors. See this stackoverflow answer.

If you are careful to follow the OWASP recommendation and always return an object such as { "token": "foo" } at the top-level of your JSON response, you need not worry about this.

@cspotcode
Copy link

JSON hijacking is also possible using a charset-based attacks. I just learned about it, but here's my attempt at an accurate summary.

Attack:

A malicious site loads your AJAX endpoint via <script> tag with a charset attribute. This causes the JSON to instead appear like JS accessing a global variable. The window object's prototype is manipulated so that accessing global variables invokes an ES Proxy trap which then reads the contents of the JSON, since it's been passed to the trap as a string.

Mitigation:

You must declare a charset in the "Content-Type" header returned by your server. This means the <script> tag's charset will be ignored. The AJAX endpoint that returns CSRF tokens must always declare a response header "Content-Type: application/json; charset=utf-8"

Details:

http://blog.portswigger.net/2016/11/json-hijacking-for-modern-web.html

@frutality
Copy link

@cspotcode and this is by default turned on if we are using popular web servers, right? I mean, I checked my sites behind nginx (default config) and charset is present everywhere. But good catch though.

@cspotcode
Copy link

@frutality I'm not sure, but I think it would be good for this document to explain both those points: that charset is required on /csrf and whether or not any extra effort is required to ensure it's presence in practice.

If nginx is reverse-proxying to an express server, are there common ways of returning JSON from a route that don't include a charset by default? Does express have a middleware to enforce charsets on all responses, throwing an error if it's absent?

@marfire
Copy link
Author

marfire commented Oct 14, 2017

@cspotcode: The attack described in that article is dependent on bugs in the browser: "Conclusion:
Edge, Safari and Chrome contain bugs that will allow you to read cross domain undeclared variables."

That's interesting and important information, but it doesn't seem to fit with the purpose of this page. As far as I can tell it's designed to present best practices based on the specified behavior of the various actors and protocols involved. If it's going to give advice based on browser bugs it will have to be much more involved and edited much more frequently, since the landscape is always changing. For example, since the article was written the bug in Chrome has been fixed, and presumably the others soon will be as well.

@frederikhors
Copy link

frederikhors commented Sep 29, 2019

Today still no clear answer in my mind: can we expose such rout for CSRF tokens for (e.g.) a /login API?

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

No branches or pull requests

10 participants