19
votes

I've done a lot of research on "best practices" surrounding this and have read blog post after blog post, SO question after SO question, and OWASP article after OWASP article. I've arrived at a few clear answers but some unknowns.

First, the "Do's":

  1. Use JWT for authorizing users on my REST API [1] [2]
  2. Store the JWT in a HTTPOnly/Secure cookie and build in CSRF protection. Do NOT store in HTML5 local storage [3] [4] [5] (Actually, this point is debatable, is it easier to protect against XSS or CSRF? [6])
  3. Verify the signing method of the JWT [7]

Now I started with the assumption that having a SPA (built with Angular) and using HTML5 sessionStorage would be secure enough for short-lived tokens, but there is a point to be made that XSS attacks can happen from a "bad actor" originating in the one of many libraries loaded in from a CDN.

For my specific use case, I do not plan on having long-lived tokens - expiration after 10 minutes of non-use but I'm still figuring out if I want to track expiration by session or use refresh tokens - StormPath recommends the former (no longer stateless?) but I believe big players using JWTs use refresh tokens (Google uses them but states you need to store them in secure, long-term storage which means HTML5 localStorage is again, out of the question).

I would like to make it so my users don't have to log back in if they refresh the page (hence the need to store the token on the client side). I would also like to use my SPA as a "mobile app" with the help of Cordova. The obvious pitfall here is that if I use cookies, there is no baked-in cookie support/storage with Cordova and I'm urged to switch to HTML5 local storage instead. Since on mobile I don't really need to worry about refreshing pages, I can just let my token live in memory and expire with the strategy I settle on.

If I take this approach, cookie-based JWT on Desktop, "Bearer" headers on mobile, I now need an authentication end-point that will give tokens two different ways, and when I authorize on the REST API side, I need to support both cookie-based JWTs (with CSRF) and header based JWT verification. This complication has me worried as I don't know if I can accurately foresee security implications here.

To summarize the barrage of thoughts above:

  • Create an authentication handler that would hand out tokens via HttpOnly/Secure cookies to desktop, and by payload for mobile.
  • On my REST API, support both methods of verification - header based and cookie-based - including CSRF protection for the cookie-based approach.

Is there any reason why I wouldn't want to take this approach? I assume if I take XSS on my SPA as a serious risk, then I need a classic login-page for authentication to set the proper cookies because if I do authentication via the SPA, then any XSS attack could potentially intercept that as well (both on mobile and Desktop)! However, on mobile, I'd need to inject the JWT into SPA, maybe through some custom DOM element (meta tag?), but at that point I can just let the SPA perform the login and not consider XSS a threat on mobile devices. Cordova packages all assets into the install package so that's somewhat better but then why not take the same approach on the Desktop version?

My application takes very little user input, it is primarily a dashboard/reporting tool. There will be a "message center" but it's content should always be user-created (by only that user) and sanitized. In my use-case then, would it be ok to deviate from "best practices" and rely on localStorage not counting XSS as a serious risk for my SPA? This would simplify this entire thing (use HTML5 sessionStorage as originally planned) and reduce complexity, which would reduce attack surface for potential security blunders. I just want to make sure I understand the risks before moving forward.

Is there no secure way to make this secure other than by building a native app for mobile and not using Cordova to convert my SPA to a mobile app? I'd hate for this to be the case, but it might very well be.

I'd appreciate all thoughts on the matter!

1
I had very similar questions. What did you end up doing?Adversus
Don't use Cordova (go native or go home!) - used JWT in Secure/HTTPOnly cookies along with in the headers of all requests (to prevent CSRF) checking the JWT at both locations.someone1
@someone1 Did you had a chance to look at this link - security.stackexchange.com/questions/100129/…Gandhi
@Gandhi - thanks for the link, it was an interesting discussion but I don't believe it is applicable to my question. Specifically, I was less worried about the security of using TLS to secure my API endpoints and ways to prevent brute force password cracking, and more interested in how to securely store a JWT in a Cordova converted application. As aforementioned, I don't think there is a way to do this without bringing in native components to the mobile application.someone1
@someone1 lol @ go native or go home, typical purist but completely inaccurate for anyone reading this. If you have a rounded understanding of HTTP and the web technologies that you are trying to use as well as your storage options on a native app, you will realise that you can implement the same on a cordova application without considerable additional risk, after all http doesn't change simply because you are calling from a native app and if you don't like localStorage, write a plugin to store how you wantObi

1 Answers

2
votes

When thinking of designing javascript based cross-platform applications to run a mobile device, many of the caveats with designing regular web browser based applications do not necessarily apply.

As far as security is concerned, whether you decide to use JWT or simple OAuth tokens, ensure that all your communications are via https.

Please use localStorage as much as you want. If you consider the anatomy of a http request, all it really is sending some text based message divided into multiple sections to a server. The header of the request is no more secure than any other part of it including the cookies. So the points of interest from a security perspective are generation/validation/invalidation of the token, storage of the token on the device and the transport mechanism of the requests.

  1. Generation/Validation/Invalidation: Generate the tokens on your server. Use some technology/strategy to ensure that there is no possibility for collisions or bleeding. Also, ensure that your strategy will allow you invalidate a token on the server which then subsequently denies access to data requested from the server on further usage of the token. It is then up to you in your app to handle the users UI journey when the server denies access to resources.
  2. How you store the token on the device is constrained to what the device OS make makes available to you. Regarding whether using a native app is better than cross-platform, I think creating a native-cordova plugin to store your token using any specific native strategy if one is unsatisfied with the "out of the box" ones (such as local storage) is possible, although in my experience this is usually overkill. Happy to be corrected on this one if anyone has a different experience.

  3. Please use HTTPS ALWAYS without exception for ALL Webservice end point communications. Is HTTPS foolproof, NO, but you wouldn't build a house without a front door simply because dedicated burglars could learn to pick locks. This secures the transport mechanism considerably.

Usually, this is all native apps have to work with too anyway.