71

I read that when using JWT, there is no need to protect against CSRF attacks, for instance: "since you are not relying on cookies, you don't need to protect against cross site requests".

However, something I do not understand: if I store the token in localStorage (as I was advised on a tutorial of the same website), what prevents an attacker to forge a malicious request by reading my localStorage instead of my cookies?

Since it was generated on the server side, I don't get how I could use a token for a client request without it being stored somewhere on the client.

Mike
  • 14,010
  • 29
  • 101
  • 161
JulienD
  • 7,102
  • 9
  • 50
  • 84

2 Answers2

164

Strictly speaking, yes, anything stored in local/session storage (which I'll call HTML5 Storage) could be stolen in a cross-site scripting (XSS) attack. See this article.

There are a lot of moving parts to consider, however.

First, there are subtle differences in how HTML5 Storage and cookies are scoped with respect to JavaScript access.

HTML5 Storage is:

  • divided between HTTP and HTTPS. An item stored in http://example.com HTML5 storage cannot be accessed by JavaScript running on https://example.com.
  • divided between subdomains. An item stored in http://example.com HTML5 storage cannot be accessed by JavaScript running on http://sub.example.com (you can do some tricks to get around this, however).

Cookies are more loosey-goosey:

  • A cookie with a domain example.com will go to both http://example.com and https://example.com unless it has the attribute secure, in which case it will only be sent to https.
  • A cookie not sent with an explicit domain will only be sent back to the exact domain that sent it. If the domain is explicitly defined to be example.com, then it will be sent to both example.com and sub.example.com. (This is the most confusing part of the cookie "spec", unfortunately, see this article).
  • A cookie can be read by JavaScript if it is running on a page with a matching domain (and respecting the secure cookie flag) unless the cookie has the httpOnly attribute, in which case JavaScript will not be able to read it.

Second, since cookies are marked with a domain, when a request is made to a server, the browser will send all-and-only cookies with a matching domain, regardless of the domain of the page that originated the request.

The last part is how a CSRF attack is accomplished (the same-origin policy only helps so much). The OWASP page on CSRF is a good resource for learning how these kinds of attacks work.

The reason storing an authentication token in local storage and manually adding it to each request protects against CSRF is that key word: manual. Since the browser is not automatically sending that auth token, if I visit evil.example and it manages to send a POST http://example.com/delete-my-account, it will not be able to send my authn token, so the request is ignored.

With the above in mind, whether to use a cookie or HTML5 Storage becomes a series of tradeoffs:

Storing the authen token in HTML5 Storage means:

  • (-) Risk of it getting stolen in an XSS attack.
  • (+) Provides CSRF protection.
  • (-) Must manually modify each request going to the server, limiting you to SPA (eg AngularJS) web applications.

On the other hand, if you store the authn token in a cookie marked httpOnly and secure, then:

  • (+) The authn token cannot be stolen by XSS.
  • (-) You will have to provide CSRF protection yourself. Implementing CSRF protection is easier in some frameworks than others.

Which option is better depends on your needs.

  • Does your authn token protect anything to do with money? You'll probably want the cookie httpOnly secure option.
  • Is the level of effort required to implement CSRF protection not worth the assets it's protecting? Then the HTML5 storage might be the right place.
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
kuporific
  • 10,053
  • 3
  • 42
  • 46
  • 13
    Brilliant answer. I am more frightened than ever now. Time to double check every text input field in my app. – JulienD Feb 11 '16 at 22:16
  • 2
    First off, same-origin protects against reading/viewing of data, it does not protect against submitting a request. Correct me if am wrong. – Zack Jul 06 '17 at 16:59
  • @Zack, you are 100% correct, the Same Origin Policy only prevents reading the response, it does not prevent sending the response in the first place. Here is a good summary of some of the subtleties of Same Origin Policy: https://stackoverflow.com/a/33324803/1354590 – kuporific Jul 06 '17 at 22:02
  • @kuporific: Well this is my point: What if in my "evil' page I add a JS (it could be the same JS or a slightly modified version as the "banking app" you use). Once you visit my "evil" page, I will instruct that JS to go ahead and submit the request which will include the auth data which is present in WebStorage. In summary, nothing manual was needed since JS was executed from your own browser anyhow. SOP in this case wont stop me, nor if auth data is in Web Storage. So no, not using cookies does not automatically means no CSRF. – Zack Jul 08 '17 at 22:01
  • @kuporific same applies: WebStorage DOES NOT provide CSRF protection as your answer states. Well implemented anti-CSRF tokens (which are created per session, valid only once, and presented in forms along with a corresponding anti-CSRF value in cookies), would constitute a much better CSRF protection in this case. – Zack Jul 08 '17 at 22:20
  • 3
    @Zack evil.com can't access the web storage or cookies of example.com, so evil.com can't get a valid auth/CSRF token. However, cookies are automatically submitted to the server for all requests, even if they are sent from evil.com. If your session token is a cookie, you need to also provide a CSRF token. However, if your session token is in local storage, you have to "manually" add it to each request, the browser will not do it for you. If the Javascript adds the token as a custom header, that would be even better because cross origin requests with custom headers are not allowed by SOP. – kuporific Jul 08 '17 at 23:20
  • 1
    @Zack However, I'm not sure how practical this is because just a simple link to another page on your site would not have the session token sent with it when clicked. This is what I was trying to get across by saying you're limited "to SPA (eg AngularJs) web applications". I'm not 100% sure what the workarounds might be for the link issue. – kuporific Jul 08 '17 at 23:22
  • @kuporific: Remember, evil.com is executed through your browser, and SOP wont stop it from sending the WebStorage of example.com. It just wont see the replies and it cannot read the Tokens, but it will still send them, and that is what we need for CSRF. This is taken care of with Javascript and your browser, there is nothing manual to be done. In all of this we are assuming there is no anti-CSRF tokens. In summary, SOP does not protect against WebStorage the way you are describing. There are many articles and papers supporting my point. – Zack Jul 09 '17 at 20:28
  • WebStorage is not as secure or better as people think when compared to cookies. WebStorage will happily send back traffic over HTTP (if for example login info was sent over HTTP in the first place), it will happily send requests of another domain or site (therefore CSRF), it is vulnerable to Javascript manipulation (therefore XSS). And because of that false sense of security, many sites or apps use it to store passwords for a long period of time, so it actually ends up being worse than cookies. – Zack Jul 09 '17 at 20:36
  • 3
    @Zack how does JavaScript running on evil.com access the web storage of example.com? It cannot. The web storage specification talks about why each domain's web storage must be private, https://www.w3.org/TR/webstorage/#implementation-risks. I don't understand what you mean when you say, "WebStorage will happily send back traffic over HTTP", web storage does not send or receive http requests. Similar to my original answer, here is an article talking about using web storage to store session cookies, http://blog.portswigger.net/2016/05/web-storage-lesser-evil-for-session.html – kuporific Jul 09 '17 at 22:39
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/148743/discussion-between-kuporific-and-zack). – kuporific Jul 09 '17 at 22:39
  • Is storing the authentication token in local file at mobile devices safe without CSRF protection ? – paradise_human Sep 27 '17 at 13:35
  • 1
    @Zack : if we have a CSRF-TOKEN in the cookie with httpOnly : false and make sure the access control allow origin is only our own domain allowed and now whenever we send a request we read that CSRF token from the cookie and set in the Authorization field of the header Can I now guarantee no CSRF Vulnerability and also make sure all my form inputs/URL are well escaped to avoid leaking cookies through XSS – j10 Sep 28 '17 at 06:35
  • 1
    Why not both? You can store the JWT in a httpOnly secure cookie and have a csrf token in localStorage and have the server expect both. People can either do XSS to get you csrf token or do CSRF with your http cookie but they can't do both. Unless you fall to a very well made phishing scam. – Alex Cavazos Nov 06 '17 at 18:05
  • 1
    @AlejandroCavazos, what you're describing sounds similar to the [double submit cookie](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Double_Submit_Cookie) defense. – kuporific Nov 06 '17 at 21:42
  • @kuporific: hmm, so when Webstorage is used to store a jwt, under normal situations, how is that jwt sent with every request the user does? You said its manually added, how so? And what would be the difference of a user actually clicking on a link to perform an action versus the same user copying and pasting the link directly to the browser to perform that same action (assuming its GET based)? – Zack Aug 31 '18 at 23:34
  • 1
    According the OWASP page on CSRF, there is no CSRF protection that can't be circumvented using a successful XSS attack: " Cross-Site Scripting is not necessary for CSRF to work. However, any cross-site scripting vulnerability can be used to defeat all CSRF mitigation techniques available in the market today.". Wouldn't that mean, that the HTML5 store without CSRF protection is equally secure as HTTP only session cookies with CSRF protection? – Richard Oct 24 '18 at 01:06
  • Token in local/session storage with secure CSP header should be good enough for SPA which accepts payments ? or httpOnly cookie is still needed? – Aks Oct 12 '22 at 06:55
6

When using token based authentication you have to manually associate the token with the request. Contrary to cookies, tokens are not set automatically by the browser thus not susceptible to csrf attacks.

While this approach is safe from csrf attacks, it is susceptible to xss attacks.

A minimal effort improvement would be to use session storage instead of local storage since session storage data gets purged after the user closes the tab/browser.

JulienD
  • 7,102
  • 9
  • 50
  • 84
senjin.hajrulahovic
  • 2,961
  • 2
  • 17
  • 32