Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement strong server-side account passwords with user-friendly client side passwords

I have a requirement to allow users to use a 4+ numeric pin style password on a single device. Obviously this is a bad password to be used directly to protect their server side account, so I want to 'lock' this PIN to the specific device, so that someone must have both the PIN and device to login, and where gaining access to the device does not give access to the PIN. Also, they already have a more conventional username/pw login, and by signing up on a device they are adding a new set of login credentials to this existing account. So this is my current plan:

  1. During the local device signup process, locally generate a strong password.
  2. Send this generated strong password to the server as their server-side device specific account password.
  3. Use their supplied 4+ digit password to locally symmetrically encrypt the strong generated password.
  4. Store this encrypted strong password on the local device.
  5. During subsequent logins on that device, the user supplies the same 4+ digit password, it is used to decrypt the previously stored strong password, and the strong password is sent off to the server for login.

So I believe this achieves the strong server side password requirement. However I also need to protect as best I can against compromised client devices after the signup process (devices compromised before/during the signup process are a lost cause). Obviously with a 4 digit encryption key, there are only 10,000 possible combinations, so an attacker will easily be able to try every combination on the locally encrypted strong password very quickly. What I want to know is do I have to choose a specific symmetric encryption scheme and/or generated password format so that the attacker will not be able to tell from local data alone which of the 10k decryption attempts was the correct one? i.e. He would still have to attempt each of the 10k passwords on the server-side login.

Also, is there anything glaringly wrong with this approach, or a more standard approach to achieving these requirements? If there is a standard approach, is there a standard .NET library for this approach?

like image 973
Tyson Avatar asked Nov 26 '12 00:11

Tyson


2 Answers

You could store a salt in the local device and calculate key1 using PBKDF2. Then use that key server side to calculate key2, using a separately stored salt. As key1 is never stored, the attacker does not have the required information.

like image 130
Maarten Bodewes Avatar answered Nov 15 '22 12:11

Maarten Bodewes


Your solution is "bad" because no matter how "strong" the encryption that you use is the keyspace that an attacker has to search is very small. Even with 8-digit numeric passwords, there are only 10^8 or one hundred million possible keys.

My approach would be different. I'd start by asking "why store any credentials at all on the device?" I don't believe there's a good answer to that question.

Instead consider this solution:

The user wants to activate a PIN. First, he chooses the PIN; let's call his choice N. He contacts your web service, identifies himself with his username and password and sends N.

On the server side, you generate a random string, let's call it X. You concatenate X and N, and then hash that using something like SHA-256. Let's call the result H. The server now stores (X, H). Notice that your server does not store the PIN.

Later on, when the user opens up the mobile device and puts in his PIN, the device contacts your web service and says: "I'm about to perform a PIN auth for user 'Nik Bougalis'. Give me a challenge!"

The server creates a state for this authentication: It locates the account record, and finds the X value and sends to the user "X,R" where R is a random number that is generated for this state.

The user puts his PIN in, let's call it P. The mobile device now concatenates X and P, and hashes the result using the same algorithm the server uses, generating H. It now combines H and R, and hashes the result. This generates a new hash, let's call it V.

The mobile device sends V to the server. The server combines the H value from the database and the R associated with the state, hashes the combination and compares it to V. If they match, the PIN entered in the mobile device is correct and you grant the user access. If they don't match, the PIN entered is incorrect.

This method has the advantage that the server can rate-limit authentication attempts and slow down brute force attacks. It can even lock an account down after too many unsuccessful attempts, requiring the password to unlock it.

If you are doing this whole exchange over SSL, you can skip sending the random value R, although the computational cost is so little that I'd just include it anyways.

A potential disadvantage is that the user cannot "login" using a PIN if the device cannot talk to the server. I don't know if you have a requirement to allow offline logins, but depending on your particular app and market, this may not be a big deal - some may even call it an advantage.

To be clear, this isn't a "super bullet-proof solution". If someone breaks in and gains access to the table holding the H values they can impersonate the user without knowing the PIN. If that's a concern, there are some workarounds there.

If you are really interested in very high security (and I doubt you are, since you are contemplating 4-digit PINs) there are more advanced algorithms that could be used to perform zero-knowledge proofs of identity.

like image 35
Nik Bougalis Avatar answered Nov 15 '22 12:11

Nik Bougalis