17

I'm developing an SPA with REST back end and want to have simple token based authentication. The goal for REST backed is to be stateless. I will explain the security model and try to reference all sources for decisions that were made during design. Would want a comment about the whole setup and answer to some specific questions.

Abstract

Authentication will be done on a dedicated endpoint (for example /auth/login). Login information will be passed as a JSON object. Information will contain username and password. Upon successful login, a signed JWT token will be issued. This token will be used for subsequent access to REST API.

Application will use HTTPS and I control all of the subdomains.

Specifics

JWT

  • Contains username, issued at and expiration time
  • Contains claims about role membership inside an application
  • Contains a CSRF token (randomly generated from a cryptographically secure pseudorandom generator)
  • Signed with a HMAC (SHA256) key only available to the server

Expiration

A token will be issued with a short expiration time (for example 30 minutes). An updated token with new expiration will be issued at every request (sliding window expiration) with a deadline of something reasonable (for example 8 hours). The process flow will be as follows:

  1. Initial login request will issue a token with expiration of 30 minutes
  2. Next request will check if the token has expired. If it is expired, access will be blocked. If not, a request will be fulfilled. A check will be done at that point about the age of the token.

    • If the age is within the deadline (less than 8 hours old), updated token will be generated containing new expiration time for another 30 minutes. Issued at time will remain the same as in original token.
    • If the age of the token is pass deadline, no updated tokens will be generated and user will need to login again.

Rationale: I want to protect a token with a short expiration time in case it is compromised. I also don't want to force the user to relogin often. An attacker would need to request a new token in a timeframe less than expiration time of a token. In case an attacker manages to do that, maximum amount of time he/she can use the token is defined by the deadline. User is able to stay online for a period of set deadline. For a business application a deadline of 8 hours or a day is sufficient. Is this implementation safe enough? What are possible drawbacks and vulnerabilities?

JWT transport and storage

JWT will be transported as httpOnly cookie to the client and will be stored in Cookies repository on the browser.

Rationale: To protect a token from XSS attack which would compromise the token if held inside localStorage/sessionStorage. This is per a recommendation from this article. Anything wrong with this approach other than opening the implementation to a CSRF attack?

CSRF protection

Storing JWT tokens inside cookies will open the implementation to a CSRF attack. To mitigate this problem, a double submit cookie method is implemented. CSRF token is generated and included inside JWT token. This token is also supplied to the client application in a JSON object of a response to the login request. Application will store CSRF in localStorage and submit it with every request inside a custom header.

Rationale: As per an answer to this question referencing this paper, for an effective CSRF protection it is necessary to cryptographically tie the CSRF token with a session identification/authentication cookie. If I include a token inside a JWT, is it considered cryptographically tied to a security session?

Conceptually, do you see anything wrong with this implementation?

Marko Vodopija
  • 1,062
  • 1
  • 8
  • 19
  • I'm currently tryin to find a proper solution for myself and everything always boils down to "Store the jwt in the localStorage and send it with each request". Your solution seems fine but if you take a closer look at it, you are storing the CSRF token in the localStorage. I wanted to store the jwt in cookies to avoid using the localStorage this way but even you fell back to using it. Am I wrong somewhere? Is there really no solution for this case other than storing in the local storage? – Telokis Aug 08 '18 at 09:15

1 Answers1

5

Is this implementation safe enough? What are possible drawbacks and vulnerabilities?

JWTs are self-describing Bearer tokens. The two major drawbacks of JWT are stale tokens and the inability to expire them on-demand.

Stale tokens

If the role of your user changes, it will not be reflected in the issued token. You will need to issue a brand new one. Ideally, you would expire the one before, which brings us to the next problem.

No on-demand expiry

JWTs cannot be expired on demand as they are themselves stateless. An approach to solve this is JWT blacklisting, but that will kill your server's statelessness. This also means that your user cannot essentially log out of your application since you cannot invalidate tokens on-demand.

About the short lived tokens. If the attacker is in possession of a short lived token, he can just query your API to "keep it alive" for as long as you let him (8 hours or 1 day).

You will need to decide if this is safe enough. It all depends on what trade-offs you are willing to make.

Anything wrong with this approach other than opening the implementation to a CSRF attack?

This works perfectly. I have seen a lot of websites use it. You do need to worry about CSRF though. Unless your API is JSON only. See here.

If I include a token inside a JWT, is it considered cryptographically tied to a security session?

Yes, although you cannot rotate this secret effectively because of the on-demand termination problems I described above. This is, again, a trade-off.

Conceptually, do you see anything wrong with this implementation?

I, think what you described is perfectly reasonable. It does have its drawbacks and trade-offs, but only you know the exact business case you need to support. If no logout, stale tokens, and a fixed CSRF token per session are acceptable, then it's all good.

Daniel Szpisjak
  • 1,825
  • 10
  • 19