8

I have an access code that is sent to users so that they can access the site in one-click. From terms of access it's very similar to the password reset url, so I assume the same assumptions apply.

E.g. in this question people suggest to hash this type of token which sounds reasonable, but the problem is: if I use bcrypt, how do I store it so that I can query it back or ensure uniqueness during generation? I don't see any way apart from checking each and every token in the database.

Does that mean bcrypt cannot be used for that purpose or I am missing something?

Ilya Chernomordik
  • 2,197
  • 1
  • 21
  • 36

2 Answers2

11

Since you know that your plaintext tokens are unique (or at least this is a logical inference) you don't need a salt. The salt's intention is solely to provide hash-uniqueness in the case of identical passwords, but since your input space is intended to be guaranteed unique, you don't have this problem.

Additionally, since you have control over the randomness and size of the plaintexts, you don't need to worry about weaker passwords being cracked through traditional means, so bcrypt isn't really necessary to slow down the process. If your input space is large and random, you can just hash it with a strong cryptographic hash (e.g. SHA256) with no salt. This allows you to simply check if H(token) is in the database when generating a new token (uniqueness check).

The process for validating a reset request is then quite simple: take the user ID and the plaintext token (as supplied in the reset link) and check that they match up in the database by computing H(token) again.

Alternatively, if you still want to use bcrypt, you could ensure uniqueness by prepending the random token with the user ID before putting it through bcrypt. This doesn't require you to verify uniqueness (it will always be unique due to the ID prefix) and still allows you to verify the token just fine.

Polynomial
  • 132,208
  • 43
  • 298
  • 379
  • The token is not much more secure than a password, since it' sent over SMS and I want it to look "nice" (asked a question earlier here: http://security.stackexchange.com/questions/120583/security-requirements-for-one-time-access-code-token-in-the-url) – Ilya Chernomordik May 10 '16 at 14:03
  • I wanted to avoid any Id's or anything in the url (not for security reasons though), but I was not sure if that adds any security issues either if I just do somedomain.com?user=123&token=ABC. Can user id be abused somehow? – Ilya Chernomordik May 10 '16 at 14:04
  • @IlyaChernomordik User ID in the reset link isn't really critical. If you're already assuming the inbox is secure enough to send the token to, and the site is over HTTPS, the threat model is covered. – Polynomial May 10 '16 at 14:21
  • @IlyaChernomordik Regarding the token complexity and length, just make sure it's reasonably long. 9 characters random alphanumeric, even without case sensitivity, should be more than enough to make brute-force infeasible against SHA256, assuming you use the approach discussed in the question you linked. Doing the same against bcrypt just makes it even more ridiculous. You could even group the letters into 3 sets of 3 in the SMS to make it nice and readable. – Polynomial May 10 '16 at 14:23
  • thanks for help, I'll use ordinary SHA256 for this case then. Does it make sense to check for the unique index violation (and generate a new code) or I would be very famous if I get a collision in SHA256? :) – Ilya Chernomordik May 10 '16 at 14:27
  • @IlyaChernomordik - You would be very famous. There are currently no known SHA256 collisions. – Neil Smithline May 10 '16 at 15:01
3

You can use bCrypt. The simple solution is sending your user a an Id and the Token:

https://example.com/pwdReset?resetId=123&resetKey=[your long randomly generated key]

You can lookup the hash using the id (just like you would use the username to lookup the user's password hash).

Jacco
  • 7,402
  • 4
  • 32
  • 53