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).
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!
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/
Given a cookie with the common attributes (name, id, etc), is there anyway we can identify if the cookie is a third-party cookie? By that we mean a cookie that has been placed by website B while visiting website A. At the moment, I can see no ways of achieving that but perhaps I've missed something. I'm working on a project related to user privacy online and would like to get a list of websites that left third-party cookies in user's browser. I use Mozilla Firefox Browser.
There's no way to tell when looking at the store of cookies. The issue is that a cookie is always first party with respect to some site; the third-party-ness relates to the provenance of the cookie. The only way to identify if a cookie was a third-party cookie is to examine the actual header which set the cookie and see if that cookie was set for a domain other than the originating one. Everything is made far more complex by the fact that a cookie can be set for a whole domain (thus foo.bar.com is allowed to set for .bar.com so that grill.bar.com will also see the cookie) and determining whether a suffix is a domain or not is not at all easy (e.g., some countries have multi-level domains).
The final problem is that it's easy enough for the site to request some resource from another domain for real, and set the cookie that way. That's formally not a third-party cookie, as it is being set by the domain it references, but it works in effectively the same way.
Every cookie is set for a domain. You can compare domain names to identify 3rd party cookies. But maybe I did not fully grasp your question.
Based merely on list of cookies created so far in the browser, there is no way to say if a cookie is a third-party cookie.
EDIT
What one means by "a secure cookie" is ambiguous. To clarify:
Secure as in sent over the https:// protocol — ie. cookie is not sent in plaintext. Known as the "secure flag"
Secure as in the cookie cannot be read by Javascript running in the
browser — ie.
document.cookie will not work. Known as the "HttpOnly" flag.
This edit is to clarify that the original question is asking about the 2nd case.
Original Question
Is there any way to read a secure cookie with JavaScript?
I tried to do it using document.cookie and as far as I can see on this article about secure cookies and HttpOnly flag, I cannot access a secure cookie this way.
Can anyone suggest a workaround?
Different Browsers enable different security measures when the HTTPOnly flag is set. For instance Opera and Safari do not prevent javascript from writing to the cookie. However, reading is always forbidden on the latest version of all major browsers.
But more importantly why do you want to read an HTTPOnly cookie? If you are a developer, just disable the flag and make sure you test your code for xss. I recommend that you avoid disabling this flag if at all possible. The HTTPOnly flag and "secure flag" (which forces the cookie to be sent over https) should always be set.
If you are an attacker, then you want to hijack a session. But there is an easy way to hijack a session despite the HTTPOnly flag. You can still ride on the session without knowing the session id. The MySpace Samy worm did just that. It used an XHR to read a CSRF token and then perform an authorized task. Therefore, the attacker could do almost anything that the logged user could do.
People have too much faith in the HTTPOnly flag, XSS can still be exploitable. You should setup barriers around sensitive features. Such as the change password filed should require the current password. An admin's ability to create a new account should require a captcha, which is a CSRF prevention technique that cannot be easily bypassed with an XHR.
The whole point of HttpOnly cookies is that they can't be accessed by JavaScript.
The only way (except for exploiting browser bugs) for your script to read them is to have a cooperating script on the server that will read the cookie value and echo it back as part of the response content. But if you can and would do that, why use HttpOnly cookies in the first place?
You can not. Httponly cookies' purpose is being inaccessible by script.