0

I have a frontend heavy app with a rest api in node.js. The app and the backend are on different hosts. Moreover, the app can be accessed both with http and https. The reason why is mixed content: users of the app load their own websites in iframes, and if a website is http - it won't work, broken red https at best, not loading at all at worst. Pretty ironic that something that is supposed to improve web security, is forcing me to allow using non secure connections myself. But that's not the point. So let's say I have my frontend at http(s)://app.mydomain.com and backend at https://api.mydomain.com

And I want to make it as secure as possible, considering that my users will sometimes use insecure connections. I know that I can't protect from tampering, but at least I can protect authentication token. So I use JWT. I also want to have remember me functionality. localStorage/sessionStorage would allow the token to be read on insecure http page, so I was thinking about just setting a cookie for https://api.mydomain.com on the server. This way, the frontend can never read the token due to CORS. And I would skip all these refresh token stuff. There would be just one token to be used for everything. So if a user logged in, the server would set the token cookie and set it to expire when browser session ends, the token itself would be set to expire in 1 day. If user checked remember me, then the cookie would be set for one year and the token would be set to expire in one year. Now I'd like to ask you if that's a secure way to do it? Am I thinking correct?

Another thing obviously is CSRF. I was thinking just to check the referer and leave it at that, but I heard it could block legitimate users behind proxies that remove referer from the request. So I was thinking on using JWT as nonces. Basically on init, when the user logs in/authenticates, the server returns a new token and then the token is stored in client memory and sent with each request to the server to be verified.

Here's a summary of the process:

the frontend is at http(s)://app.mydomain.com
the server is at https://api.mydomain.com

0. make an ajax call to check if the user is logged in
1. send username and password to log in
2. the server sets the cookie at https://api.mydomain.com containing the auth token
    cookie is set to expire when browser session ends, token is set to expire in 1 day
    if remember me: both cookie and token is set to expire in 1 year
3. the server also generates a different token to serve as a nonce and returns it
4. the app stores the nonce token in memory and uses it for every call

If the user is logged in, skip to step 3.

Wht do you think? Is it secure?

Maciej Krawczyk
  • 211
  • 1
  • 2
  • 9

1 Answers1

1

In general there isn't anything wrong with the authentication and anti-CSRF strategy outlined. Incorrectly implementing any part of it could, of course, expose risk but the strategy on its own seems fine.

There are a few areas of concern that are not ideal, the biggest one you already identified: supporting unencrypted use of HTTP. As you correctly pointed out, this exposes significant man-in-the-middle risk. In addition to the inherent risk for visitors or users choosing not to use SSL-encrypted communication, but also generally exposes greater risk for SSL-stripping attacks against users that intend to use SSL. Something you might consider here is making an SSL-only option available at the user (or user organization, if appropriate) level. Implementation details would depend on stack, but if you're using something like Express or Koa you could have a piece of middleware that validates that HTTP requests are allowed before returning a response. In cases where they aren't allowed you can respond with a 302 redirect to the HTTPS path.

The main advantages I can see for using a cookie versus local storage in this case are that:

  1. The HTTPOnly flag mitigates risk of token theft via cross-site scripting (XSS). This certainly would not be the only concern if you have XSS flaws however, as successful exploitation could still issue requests from the compromised session.
  2. If you determine whether the authentication is initially done over SSL (HTTPS), you can dynamically add the Secure flag to the cookie when you issue it so that SSL-stripping exposes somewhat less risk since the token would not included as long a the auth was secure in the first place. I would hesitate to call this infallible defense against SSL-stripping, but it's an easy way to add a little bit of defense in depth.
  3. I may me missing a way as I must admit I've barely ever used the localstorage API, but I think you would need to scope localstorage to the whole domain, which means if anotherapp.mydomain.com (in keeping with your example) is vulnerable, it could compromise your localstorage containing your token. With the cookie, you can scope it to api.mydomain.com and use fairly restrictive CORS headers such as access-control-allow-origin: app.mydomain.com with access-control-allow-credentials as well.

It's not clear to me whether what you're putting in the iframes is intended to in some way interact with your content - something I would certainly caution against. In an ideal case, you would be able to stipulate that all embedded content have a valid SSL cert, and just use HTTPS throughout. Without that you are simply not going to have robust enough defenses to be reasonably confident against man-in-the-middle attacks.

user18519
  • 170
  • 7