9

I'm trying to figure out how to issue a one-time use JWT token for password reset feature and still keeping it stateless. Came across Single-Use Tokens w/ JWT which basically suggests to include hash of current password which in turn gets invalidated once the password is changed successfully.

I don't feel comfortable including the password hash in JWT so I researched a bit and found JWT ID specification but that requires to store the ID somewhere so it can be checked on token validation.

So my question is, what are the security concerns if I include the current password hash in the password reset link token which is sent over email?

Is it better to use a JWT ID and add that as a column somewhere in the database instead of using the password?

Sam R.
  • 245
  • 1
  • 3
  • 10
  • 1
    JWTs are decodable by anyone so I'd be leery of including the password hash in the token itself. – Joe Mar 13 '17 at 20:02
  • @ncd275, Joe is right. The payload can be decoded even without the secret. The secret is used to `sign` the payload. – Sam R. Mar 13 '17 at 21:09
  • @norbertpy ahhhhh yes sorry, i misunderstood, thank you – nd510 Mar 13 '17 at 21:11
  • You dont need to include the hash itself, you can include enrypted hash or calculate HMAC over it and include that. But generating ID and storing it in the database will not break your stateless philosophy. You are storing user hashes in the database after all. I think it is the most secure way since you can invalidate the token on first request even if it doesnt end up resetting the password. – Marko Vodopija Mar 14 '17 at 06:36
  • 5
    Just to clarify, the mentioned article in the "https://www.jbspeakr.cc/howto-single-use-jwt/" link does not actually include the hash of the password in the JWT. Instead, it uses the password to sign the token. – hopia Jun 27 '18 at 22:04

1 Answers1

10

I hesitate to roll my own security, but just for the fun of it here is an idea.

Instead of including the password hash, follow this process when generating the JWT:

  1. Obtain the password hash
  2. Obtain a server-side secret key (256 bits entropy should do it)
  3. Compute an expiration timestamp, say an hour from the current time
  4. Create a new hash over 1 + 2 + 3
  5. Include the hash and the expiration timestamp in the JWT

To validate a password reset request:

  1. Obtain the password hash
  2. Obtain the server-side secret key
  3. Read (and check) the expiration timestamp from the JWT
  4. Compute a new hash over 1 + 2 +3
  5. Compare to the hash in the JWT

This way you don't expose the password hash, and it cannot be brute-forced without also brute-forcing the secret key (which would take millions of years). And you also preclude the hash from reuse by embedding the timestamp in it.

John Wu
  • 9,101
  • 1
  • 28
  • 39
  • This is what I had in mind without the timestamp. But that's a good inclusion to the hash function. Thanks for your input. – Sam R. Mar 14 '17 at 00:35
  • 1
    That article doesn't say to include the password hash in the jwt, it says to use the hash to sign the jwt, so it becomes a one-time secret key... And the jwt itself is one-time too, because once the password is changed, the jwt won't be verifiable any more. Which is "better" than the approach you've detailed, because a server-wide secret key isn't needed for this use case. – lonix Mar 14 '19 at 10:03