Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JWT with SPA without a server

The application is a SPA that is hosted on static storage (conceptually similar to S3, although it's not S3) and has no backend server at all. Assume this is https://static.example.com/app.html

When users visit the page, they can authenticate with an external provider like Auth0 and Azure AD. They complete the authentication flow and are sent back to the SPA with an id_token on the URL fragment. For example, https://static.example.com/app.html#id_token=XX. That id_token is used to call an external API server, passed in the Bearer authorization header.

The issue is where to store the JWT in the client.

  1. It's a known fact that storing the JWT in sessionStorage could lead to the tokens being stolen with XSS attacks (or malicious code added in a dependency, etc).
  2. The recommended approach would be storing the JWT in a cookie that is set to HttpOnly, or at least part of it (see the "Cookie Split" section). However, this is not doable in my case, as there is no backend server, and after being authenticated users are redirected to the SPA directly, so I can't create a HttpOnly cookie.
    A variant of this method is what OWASP recommends: using a "fingerprint cookie". This has the same issues since I can't set a cookie that is HttpOnly.
  3. Another approach, as for example suggested by the Auth0 documentation, is to keep the JWT in memory. While this should prevent theft with most (if not all?) XSS attacks, it's unpractical because the session would be limited to the current tab, and would not survive a page reload.

I see two different options, all with serious or potentially serious drawbacks:

  1. Store the token in sessionStorage anyways, assuming the risk that in case of XSS attacks (or a malicious dependency injected via NPM) could lead to sessions being stolen. This could be mitigated by setting a short lifespan to tokens (e.g. 1 hour). While the app I'm working on doesn't store critical information (it's not banking or similar), a mistake in the code that let sessions to be stolen via XSS would not be nice.
  2. Implement a backend server to move the authentication flow there, maybe even replacing JWT's with session tokens entirely. This would make the application not static anymore, however, and it's undesirable.
  3. (The third option, keeping the JWT in memory, is excluded because of the poor user experience)

What am I missing?

like image 332
ItalyPaleAle Avatar asked Oct 19 '18 00:10

ItalyPaleAle


1 Answers

Security is a compromise - you can choose to accept the risk of XSS, or to take the burden of a backend server for more security, or to sacrifice user experience for a level of security probably inbetween the two. You can't have everything.

One usual solution is to have very short-lived tokens in sessionStorage, and an httpOnly cookie for the identity provider to get a new token when needed. This way stealing a session token provides less value for the attacker, and as XSS needs user interaction, it may sometimes be difficult for an attacker to get hold of a new one (or easy, depending on where the XSS is). Also this solution needs more graceful error-handling and results in slightly more complex code.

like image 82
Gabor Lengyel Avatar answered Sep 29 '22 17:09

Gabor Lengyel