Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hashing a 5 digit passcode for a mobile app with bcrypt

Our application stores normal passwords in hashed format using bcrypt, with a cost (number of rounds) of 15. Checking the validity of a password takes little over 1 second on our production servers, which is acceptable for a once-per-session action.

We're now in the process of developing and securing our app. We will give the user the opportunity to setup a 5 digit passcode, after having logged in with their usual credentials (login and password) for the first time.

Would it be reasonable to use a lower bcrypt cost (i.e. 13) to hash these passcodes?

My considerations:

  • A lower cost allows quicker access, which is vital for our app users.
  • A lower cost doesn't impose a big security risk, as the passcodes alone are useless (they are device-bound).
  • It's still strong compared to many alternative hashing mechanisms.
like image 415
Sherlock Avatar asked Oct 27 '25 06:10

Sherlock


2 Answers

Would it be reasonable to use a lower bcrypt cost (i.e. 13) to hash these passcodes?

In a nutshell, yes. Or scrap bcrypt completely (see below).

I think there's some confusion here about key stretching and KDFs (i.e. bcrypt), and how they relate to offline and online attacks.

Algorithms such as bcrypt are there to prevent the password from being known if somebody extracts the hashes from your server and proceeds to run an offline brute force or password guessing attack against them.

Bcrypt isn't there to prevent password guessing attacks against your application when running. Here you can use server-side application logic in order to detect a brute force against the same account, and then rate limit that attempt. For example, purposefully delaying the response once a certain number of incorrect guesses have been made in a certain time period against the same username.

The device registers itself on the server with a string consisting of time, device id and a random string.

Assuming the device id is a 128 bit UUID, you already have an identifier with 128 bits which is actually uncrackable if properly random. However, GUIDs are designed to be unique, not random and as you mention in your other question, anyone knowing this value is already halfway there to cracking your authentication.

I would scrap this and simply generate a random 128 bit string to use as an "installation ID" using a cryptographically secure pseudo random number generator (CSPRNG). As this is already strong, there is no need to salt it or run it through a a password stretching algorithm, or key derivation function such as bcrypt. SHA-256 hashing on the server side stored version would be adequate.

A 5 digit passcode can be represented using 17 bits. A bcrypt cost of 13 would effectively give this passcode the strength of 30 bits. This is well below the NIST recommendation of 80 bits.

However, let's have a think about what this means. Bcrypt is there to protect server side hashes from being guessed using a word list (or in this case a number list ranging from 00000 to 99999). If your 128 bit installation ID is stored hashed on the server and an attacker manages to gain access to your user table, even if they do crack the 5 digit passcode stored in bcrypt, they cannot then log in as the user as they do not know how the SHA-256 hash was generated - they do not have the installation ID. It would be completely unfeasible to run through every possible 128 bit value and find out if it hashed to the stored value. Rainbow tables would be far too big to store too with 2128 combinations.

To make things simpler I would be tempted to scrap bcrypt for the passcode codes and simply store SHA-256(passcode + 128 bit random installation ID) on the server. The passcode can act as a salt for the "installation ID" so a user changing their passcode will generate a completely different hash that is stored server-side.

Note that the above is assuming that there is a one-to-one relationship between device and account. If multiple accounts are used per device, then you should add an additional salt per account (say 64 bit) - otherwise two users that have chosen the same passcode will have the same server-side hashed value. There is also an additional security risk in that the "installation ID" is now known by another user, as well as all the additional risks that come with sharing a client device with other users (e.g. physical security aspects or malware risks).

like image 71
SilverlightFox Avatar answered Oct 30 '25 01:10

SilverlightFox


There's not much interest in hashing a 5-digit passcode alone, as it can easily brute-forced (there are only 100000 possible values). I'd try to see if there is a way to store the passcode hashed together with the user password.

like image 44
dr_ Avatar answered Oct 30 '25 01:10

dr_