Do cookies protect tokens against XSS attacks? [closed] - javascript

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I'm building a JWT-based (JSON Web Token) authentication mechanism for an browser-based Javascript web app, working with a stateless server (no user-sessions!) and I want to know, once and for all, if using storing my JWT token in a cookie will protect my token from XSS attacks, or if there is no protection, so there's no real advantage over using browser local storage in my Javascript app.
I have seen this question asked and answered in SO and in many blogs, but I've never seen an answer that really satisfies me.
This question was originally held on the basis that it solicits opinion - and given my original wording, rightly so. So let me make it clear here and now that I don't want an opinion based on vague notions of developer laziness or such - that's what the ground rules are meant to eliminate. What I want is an evidence-backed Yes/No answer. Either:
"Yes, cookies can be protected from XSS and CSRF and here's how" or
"No, by protecting your cookies from CSRF, you always open them up to the same kind of XSS attack that made cookies a good idea in the first place"
So I'm going to restate the question, with some simplifying ground-rules, and point out the holes in advance, so that you, the experts, can set me straight.
Ground rules
Your app is a javascript browser app - it might be in AngularJS, but it might be custom-built. It communicates with the server via REST calls. Let's say, jQuery $ajax calls.
The server is stateless: there is no session management.
The app users JWTs as the main authentication token ('access token' in OAuth2 parlance) and validates them on the server using a secret signing key
Ignore other important advantages of cookies: browser management, less chance of coding poorly, etc. For this battle, I want to consider the absolute security, and assume we can competently code either mechanism.
Ignore other disadvantages of cookies, such as non-browser apps, etc. For this battle we are only concerned with a browser-based javascript app.
It doesn't matter whether you use a header or a request body to transmit tokens in the non-cookie approach; nor does it matter if you're using local storage vs session storage - ignore any security differences there. And yes I know that technically Cookies use headers, ignore that.
In a nutshell, we're only interested in comparing browser-handles-tokens vs your-javascript-handles-tokens and the comparative XSS and CSRF security risks.
The contestants
In the red corner, Auth0: Local Storage beats Cookies, because XSS is easier to fix than CSRF
In the blue corner, Stormpath: Cookies beats headers, because actually CSRF is easier to fix than XSS.
(excerpts of both arguments in detail below)
Weapons of choice
XSS and CSRF (we'll use CSRF and XSRF interchangeably: the C seems to be more popular in documentation, the X in code)
Here's my super-simplified summary of the attack types:
Let's assume your stateless, JWT-authenticated, javascript browser app is for online banking and the attacker, "Evil Corp", wants to submit an AJAX REST call that transfers funds to their account by impersonating your users.
XSS (Cross-site scripting)
(As Stormpath points out, there are many attack vectors - I'll pick one)
Evil Corp buys the github account rights for the nifty text field widget you use for password entry. They know your bank site uses it, so they update it to submit AJAX requests to transfer funds to their account when you type in your passord and hit enter. Your build system foolishly pulls the update and puts in production.
CSRF (Cross-Site Request Forgery)
Evil Corp knows your bank site uses JWTs in cookies to authenticate transactions, so they write a web app that submits AJAX requests to transfer funds to their account. They host this on their own evil.com site, and lure you there with an email (phishing) or some other way, when you happen to be logged into your bank site in another tab. The browser submits the request from evil.com, but attaches your JWT becaues it's going to the correct site: the bank.
Standard defences
The defence against XSS is to be very careful about the code in your site so that you never let the browser process something the user types in without sanitizing it (removing javascript and html) and that all the 3rd party libraries (Evil's text field widget) are vetted before being used. As Stormpath rightly points out, this is hard, bordering on impossible.
The defence against CSRF is to use a form of double-submit-cookie. This means our server creates a token (securely random string) and sends it to our Javascript browser app in a readable cookie (call it "XSRF-TOKEN" by convention), and our Javascript sends it back in a header or body with every request.
Actually, double-sumbit-cookie's are only one defence agasint CSRF, but some others require stateful server sessions and no other (I think!) offers any better protection. The statelessness can be achieved by also putting the token in the JWT and comparing it on the server side with the one that comes in the header or body.
But the real CSRF-busting quality of this defence, is that same-origin-policy mean that only the javascript that our app loaded from our domain can read that cookie. So even if the javascript on evilcorp.com can send our cookies with its requests, it can't embed our XSRF-TOKEN because it can't read it in the first place.
To really simplify CSRF:
CSRF attacks work because a browser attaching a cookie depends only on the destination of a request.
CSRF defences work because Javascript access to a cookie depends on the origin of the Javascript.
Auth0's argument
It's easier to deal with XSS than XSRF Cookies have this feature that
allows setting an HttpOnly flag from server side so they can only be
accessed on the server and not from JavaScript. This is useful because
it protects the content of that cookie to be accessed by injected
client-side code (XSS). Since tokens are stored in local/session
storage or a client side cookie, they are open to an XSS attack
getting the attacker access to the token. This is a valid concern, and
for that reason you should keep your tokens expiration low.
But if you
think about the attack surface on cookies, one of the main ones is
XSRF. The reality is that XSRF is one of the most misunderstood
attacks, and the average developer, might not even understand the
risk, so lots of applications lack anti-XSRF mechanism. However,
everybody understands what injection is. Put simply, if you allow
input on your website and then render that without escaping it, you
are open to XSS. So based on our experience, it is easier to protect
against XSS than protecting against XSRF. Adding to that, anti-XSRF is
not built-in on every web framework. XSS on the other hand is easy to
prevent by using the escape syntax available by default on most
template engines.
https://auth0.com/blog/2014/01/27/ten-things-you-should-know-about-tokens-and-cookies#xss-xsrf
Stormpath's argument
Stormpath recommends that you store your JWT in cookies for web
applications, because of the additional security they provide, and the
simplicity of protecting against CSRF with modern web frameworks.
HTML5 Web Storage is vulnerable to XSS, has a larger attack surface
area, and can impact all application users on a successful attack.
https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage/
Also:
I see a lot of discussions where cookies are pitted against access
tokens. While we’ve all been burned by systems that store a session ID
in a cookie, and that cookie is not secured and thus gets stolen. That
sucks, but its not a reason to use tokens. Its a reason to avoid
non-secure, non-https cookies.
https://stormpath.com/blog/token-auth-spa/
My take
Stormpath's argument in favour of cookies is pretty convincing, but there's a hole in it that I don't see them addressing clearly:
The double-submit CSRF defence relies on the fact that my CSRF attacker cannot access my cookie: the one with the XSRF-TOKEN in it. But isn't that cookie just as vulnerable in an XSS attack as local storage?
An XSS exploit can run javascript on my domain, so it can read the same cookies my javascript can. (The browser doesn't know that it isn't my Javascript)
To look at it from the other side: local storage is protected by the same-origin-policy just as much as a readable cookie. If I'm using the Auth0 approach, and an XSS attacker knows how to find my JWT in local storage and use it. Can't that same attacker use that same XSS script to grab my XSRF-TOKEN cookie and use that?
Both attacks require them to read and understand my javascript app, but that's out there in their browser.
So what's the difference? Is one really more secure than another, and why?

Abandon all hope unless you can secure against XSS!
Or
Choose the approach that suits you based on other criteria because both are equally secure, equally insecure.
If you use cookies, you should definitely use the double-submit-cookie defence, or something similar, because it does protect you against CSRF in the absence of XSS. That is, if you don't do this, you're definitely open to CSRF attacks - from other domains - that don't even require XSS exploits to work.
But either way, your source code is publicly available (JavaScript in your browser) so for a motivated hacker, there is no significant difference in effort between finding which token to pull from local storage and reading your XSRF-TOKEN cookie. If Evil Corp can get some JavaScript running in your domain - that's XSS - then you're hosed.
Non-security-related criteria you might want to consider for your choice:
Cookies are convenient because you don't have to write JavaScript code to manage the token - only the XSRF.
Redirection becomes a little more automatic too, if you want to use it.
Local storage is easier to adapt to non-browser apps - from the server perspective that is, because if you write say, an Android app in Java that doesn't want to deal with cookies, your server doesn't need to make any distinction between in and the browser, since it's not using cookies.
Anyway, make up your own mind, but be careful about the JavaScript you write and the 3rd party JavaScript you use!

Related

Any benefit of enabling JWE if I am using HttpOnly for JWT

Goal:
When using JWT, I need to secure cookies and mitigate XSS attack vector in clients.
I understand that a cookie with the HttpOnly attribute is inaccessible to the JavaScript Document.cookie API in the client. The server can access the cookie to for session management (shipping cart, game scores, etc), personalization and, user behavior tracking but JavaScript code can no longer read and write to the cookie in the client.
Reference:https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
Question:
If I am able to enable HttpOnly, is there any practical gain to encrypt the JWT (JWE)? I see that encryption for the JWT is described on https://www.rfc-editor.org/rfc/rfc7516.
I have not seen posts to indicate that JWE has been a practical choice though.
I would appreciate your insight before I explore the deployment of JWE in production.
One potential benefit would be that if the JWT is encrypted, the client won't be able to view and tweak it manually. Even if there aren't any malicious scripts running, there may well still be several inquisitive users who know how to open the developer console and change things - to see what they can break, or gain privileges they shouldn't, or just for the heck of it. If the JWT is encrypted so that only you on the server can decrypt it, that makes things easier for you because the client has no way to change the payload without breaking it entirely (unless they have another JWT saved or found somewhere).

Security on $https requests

Good Morning,
I'm developing an app in ionic and there are some $http requests(angular) that send data to a server controller(yii 1) to save data on database. I finished my app, but it doesn't have security. I was wondering how to protect it, because right now anyone if look my $http request can know what parameters have to send, and kill my app.
What I should do to protect it? Maybe through tokens? I'm really lost with security methods.
Thank you so much,
Carles.
well When you research web application security you will come across Cross-Site Request Forgery (CSRF). This attack vector is taking advantage of cookies, but in a preventable way.
Most attacks focus on stealing your cookies because nearly every website uses cookies as a form of authentication. The setup is this: when a user logs into your server, you set a cookie in the browser. This cookie contains a unique ID which is a link to the user’s session information in your database. The browser supplies this cookie on future requests, and the server knows who you are.
On the surface this sounds not-so-bad, but here is the catch: the web browser can be tricked into making requests to your server, even if the end-user didn’t perform the action themselves.
Using POST Requests
It is sometimes thought that using proper form-based POST requests will mitigate this attack, but that is not true.
Using HTTP-Only or Secure cookies
While you definitely should use these flags on your session cookie, they don’t implicitly stop the attack: the browser still sends the cookies to your domain when a request is made to your domain. Your server does not know if this is a real user or an attack.
How To Prevent CSRF
You can achieve this by relying on a set of rules that browsers respect, called the Same-Origin Policy. This policy asserts that certain sensitive operations are performed by JavaScript code that is running on our website, not some other website.
Angular packages the CSRF token approach, making it simpler for us to implement. For every request that your Angular application makes of your server, the Angular $http service will do these things automatically:
Look for a cookie named XSRF-TOKEN on the current domain.
If that cookie is found, it reads the value and adds it to the request as the X-XSRF-TOKEN header.
Thus the client-side implementation is handled for you, automatically! But this does leave the server side pieces in your hands. You will need to do the following parts:
During login: create the CSRF token (with a random, un-guessable string), and associate it with the user session. You will need to send it on the login response as the XSRF-TOKEN cookie.
Assert that all incoming requests to your API have the X-XSRF-TOKEN header, and that the value of the header is the token that is associated with the user’s session.
That’s it! With a little bit of backend work, you now have a strategy which protects you from CSRF attacks.
you will find a working example of how to prevent CSRF attack here
and for theory of understanding CSRF attacks please follow this reference

jwt authentication: cookie vs header

There are a lot of articles around discussing what is the best place to store JWT on the clientside. In short, they're all about -
Http-only secure cookie - no XSS, but vulnarable to XSRF
Header (saved in local storage or DOM) - no XSRF, but vulnarable to XSS
I think I come up with an extremely savvy solution to this, but, since I'm complete noob in security I'm not sure if it's really savvy or stupid.
So, what if to split JWT and save part of it in the cookie and another part in the header? Would it be unbreakable?
This should also solve 'logout' problem - deleting header portion would make browser incapable of logging in.
The JWT needs to remain together, otherwise the signature validation won't work.
Protecting against XSRF is pretty easy, you just need another cookie.
Never use local storage for storing authentication information, it doesn't follow the same domain and origin rules as cookies. Read more here:
https://www.owasp.org/index.php/HTML5_Security_Cheat_Sheet#Storage_APIs
Disclaimer: I work at Stormpath, we have a hosted user management solution and we spend a lot of time on security. I've written two blog posts where I discuss JWTs and front-end auth:
Token Based Authentication for Single Page Apps (SPAs)
https://stormpath.com/blog/build-secure-user-interfaces-using-jwts/

What are (if any) the security drawbacks of REST Basic Authentication with Javascript clients?

I have this application that consists of a REST back-end intended to servicing requests from an HTML5/JavaScript client (which I'm also building).
I'm planning on implementing an authentication mechanism that uses Basic Authentication where the JavaScript client would store the Base64-encoded user's credentials for the duration of a session. These credentials would be sent with each REST request in the "Authorization: Basic" header.
All the conversation between the JavaScript client and the REST backend would occur over HTTPS. I'm know that's a performance drawback in itself as it adds the overhead of encrypting/decrypting every single request/response, and that's ok for now.
What I'm really interested in at this point is the security aspect of it. I know the schema I've described is nothing novel and a lot of people have used it in their implementations (at least that's my understanding). However, I'm interested in knowing if anyone has encountered any security breaches or drawbacks with that.
The only thing I can think of would be if malicious code on the client side could somehow gain access to the stored credentials… I think thats is highly unlikely (but hackers are a creative bunch and some JS engines are buggy, so you never know :-)). Thoughts?
The “hard” credentials should never be stored in an area that is accessible by Javascript, otherwise you open yourself wide to XSS attacks.
I recommend using access tokens and storing them in HTTPS-only cookies. You do an initial exchange of hard credentials for access token, then use the token (which is time limited) for subsequent requests.
I have written a lengthly article on this subject and It covers my answer in detail: Token Based Authentication for Single Page Apps
Hope this helps!
CORS issues aside (assuming you're making rest calls to your same domain), the big concern is the client would need to have the credentials inside the javascript. Anyone would be able to read your code and use them (as you've pointed out).
Even if the credentials are just the users own, anything in your client side could be in danger of exposure by cross site scripting or any browser plugins that can manipulate the DOM (I'm thinking for example things like the selenium testing IDE)
Basic authentication is really basic ;-) You don't really control the session, ... Here is a link about a more advanced approach (token-based authentication) for RESTful services: https://templth.wordpress.com/2015/01/05/implementing-authentication-with-tokens-for-restful-applications/.
Otherwise I agree with the previous Robert's answer that we need to be very careful when storing credentials in the client side (XSS attacks).
The problem with cookies is that your client needs to be a browser to leverage this feature transparently... If it's the case, you can leverage this. If you're opened to any REST clients, it could be a problem since clients need to handle cookies manually. Moreover it's really not the better approach for authentication within RESTful services ;-)
I don't really see other approaches (exception of cookies) to implement authentication in SPA in a convenient and flexible way. Notice that JavaScript frameworks like Angular provided supports to prevent from XSS attacks.
I give an answer here about such issue: Is there any safe way to keep rest auth token on the client side for SPA?.
Hope it will give hints to your issue.
Thierry

How do you secure a RESTful API being consumed by a browser from CSRF attacks?

I'm designing the API for a group of sites. The sites are very similar (kind of like StackOverflow, SuperUser and ServerFault), and it makes sense for them to have a shared backend. Hence we decided to try and have a nice REST API as a backend, and a bunch of very-similar-but-different frontends consuming said API. The frontends should preferably be all static, but that's not a hard requirement if it turns out to be borderline impossible.
I'm working on designing that API now, and I'm worried about the security implications, particularly CSRF. From my basic understanding of CSRF attacks, they consist of two important components:
Being able to name the resource and the request body.
Tricking the user/browser into using ambient auth (like sessions) to make a request to that resource that looks authenticated.
A lot of the classic approaches to fixing CSRF attacks are based on the session. Since my REST API doesn't really do sessions, that both prevents a lot of the vectors and also pretty much all of the ways to fix them. For example, double submitting doesn't make sense because there's nothing to double submit.
My initial approach involved attacking part 2 of a CSRF attack. If I authenticate all the requests (say using HTTP Basic Auth), and the browser doesn't keep those credentials stored (e.g. some JS made the request), only the JS that has the credentials can make the request, and we're done. The obvious downside is that the app needs to know the user's credentials. The other slightly less obvious downside is that if I want to store credentials securely on the API end, then verifying a password should take a fixed, non-trivial amount of time. If verifying a password securely takes 100ms, then every other request is going to take at least 100ms + eps, and it's going to take some darn clever clientside trickery to make that not feel slow. I might be able to cache that (since the credentials will always be the same), and if I'm very careful I might manage to do that without introducing a timing vulnerability, but that sounds like a hornet's nest.
OAuth 2.0 seems a bit over the top, but I guess it might be the best solution after all, lest I end up implementing it poorly. I suppose I could do the HTTP Basic Auth thing for now, and move to OAuth when we have third party app developers.
There's a bit of an impedance mismatch with OAuth. OAuth really wants to help apps access stuff on another app, basically. I want users to sign up on one of the frontends, before such an account even exists.
I've also considered attacking point 1 by making the URLs randomized -- ie adding tokens to a query string. This would certainly work and it's very close to how the traditional randomized token in a form works, and given HATEOAS it should even be fairly RESTful, although this raises two questions: 1) where do you start? Is there a mandatory API start point where you do log in using HTTP Basic Auth? 2) How much would it make app developers happy if they can't predict a URL up front, HATEOAS be damned?
I have seen How to prevent CSRF in a RESTful application?, but I disagree with the premise that randomized URIs are necessarily unRESTful. Also, that question doesn't really have any satisfactory answers, and doesn't mention OAuth. Also, the session double submit solution is invalid, as I've mentioned above (different domain for the static frontend than the API endpoint).
I realize that what I'm fundamentally trying to do here is trying to allow cross-site requests from one domain and disallow them from the other, and that's not easy. Surely there has to be some reasonable solution?
A CSRF token is by definition "per user state" and therefore not RESTful. Most API's break their "per user state" requirements for the purposes of security, and require a CSRF token passed as an HTTP header parameter.
There are other ways of preventing CSRF. Checking the referer is not as strong as a CSRF token, but it is RESTful and VERY unlikely to be undermined. Keep in mind that the lack of a referer should be considered a failed request.
XSS can be used to bypass both token based CSRF prevention and referer based CSRF prevention.
The answer depends on what you need to support. If we assume that you want to support a web-app which uses the REST service and use the same REST service for an API that is something different to a web-app that happens to be RESTFUL (you may decide that 'sessions' are for you! ).
For now (/me is rather tired) I think the way you have suggested using javascript with HTTP Basic Auth is a good start.
I have another potential solution, so I'm listing it as an answer. Please feel free to take it apart :)
auth.platform.com takes authentication and sets cookies. If auth.site.com is a CNAME for auth.platform.com, would the request to auth.site.com (ending up at auth.platform.com after resolution) be able to set a cookie for site.com? That way I could double-submit session cookies.
Of course, auth.platform.com will only set cookies for a few whitelisted domains.
EDIT: Except of course this won't work at all, because you'd have to use HTTPS to do the authentication securely, and HTTPS will see right through your trickery.
Designing your API as a true RESTful one, prevents the most common CSRF vectors:
avoiding cookies prevents them to be "stolen",
using GET for "safe" operations prevents img and iframes to trigger unsafe actions without user interaction.
Then you should implement CORS to let users' browsers block requests from origins you don't trust.

Categories

Resources