1

I am trying to add a small proof of work function to protect a password from brute force attacks. The solution I have so far is based on the security iPhones have on their lock screen passcodes. I'm repeatedly hashing the password to achieve this.

import CryptoJS from 'crypto-js';

function sha256_digest(content) {
    return CryptoJS.SHA256(content).toString(CryptoJS.enc.Hex);
}

function hash_n_times(content,n) {
    let hash = content;
    for (let _ = 0; _ < n; _++) { hash = sha256_digest(content); }
    return hash;
}

let password = hash_n_times('Password12345',100000);

However, for iPhones, as I understand it, the way the password is stored is device specific and hardware dependent. So they only need to ensure that their work function would take time (like 80ms I heard) to compute on that particular hardware. On the other hand, my application is going to be used on the web so, I have no way to tie this to hardware, so I would like my work function to hold up (at least to some degree) to attacks from other hardware.

The issue is there seems to be a huge discrepancy between the hashing power of a mobile phone or PC compared to crypto mining hardware on the market. With my Mac on Chrome, I can get like 350K hashes in a second. Meanwhile the latest Antminers are doing over 100TH/s...

Assuming a password input that's 8 characters, case sensitive, and alphanumeric, to brute force every permutation it would take 62^8 attempts. Let's say I use 100K hashes so slow mobiles don't freeze up, a single Antminers could brute force it in 62^8 * 10^5 / 10^13 seconds or approx 25 days.

Users are pretty bad an generating random passwords, so that's really worst case, probably most passwords would get broken much faster. So I need a solution that is ideally a few orders of magnitude stronger.

Unfortunately due to hardware limitations, I can't really hash that much more on mobiles. Maybe there's a better operation than hashing or something to be done in addition to hashing? I can't really make users use longer and safer passwords because in practice they will forget them, and making a friendly password recovery system will reduce the effective security as a whole to that of the recovery system.

I also have a salt (and even pepper), but I'm not really sure it helps in this case against brute force on the password itself since the salt is usually stored in the database anyways, so if the attacker has the password hash, he also has the salt. For pepper, I'm looking at worst case where everything is compromised, so while I implement these, I would like to have a more robust foundation for my work function to be harder to intrinsically break.

How do I do this more effectively?

Anters Bear
  • 1,816
  • 1
  • 15
  • 41
  • 3
    don't do that - just record the number of failed attempts and lock out a certain amount of time. or enable mfa – Daniel A. White Jan 11 '23 at 15:32
  • Please clarify: is this an *offline* app, or is there a server involved as well? – deceze Jan 11 '23 at 15:34
  • Thanks for your comment @DanielA.White this is to deal with the case of data breaches and compromised server. We have rate limits in place, but this doesn't help if the whole database is breached. – Anters Bear Jan 11 '23 at 15:42
  • @deceze it's not an offline app, but the case I'm looking at is of a data breach, so the attacker can do whatever he wants to the data. Rate limits won't protect against it – Anters Bear Jan 11 '23 at 15:43
  • So in the end a *server* will do the actual password validation, yes? Then why not hash on the server, as is the common practice? – deceze Jan 11 '23 at 15:44
  • @deceze ideally we would so that we are never in procession of the password. But even if we did, we might get like 500K Hash per Sec? That's still not good enough compared to a single mining rig. We're on the cloud right now, so custom hardware server is a bit hard. – Anters Bear Jan 11 '23 at 15:48
  • if the database is already compromised then you are out of luck. – Daniel A. White Jan 11 '23 at 15:51
  • @DanielA.White yeh I mean if we could just hash or have a hash function that works faster on regular devices then this method could actually work – Anters Bear Jan 11 '23 at 15:54
  • It is extremely common to hash passwords on the server using an appropriate *slow* algorithm which is hard to execute fast, even using specialised hardware. 1) The hash is stored on your server, and if you do everything right, no attacker will ever see it to begin with. 2) If you do have a data leak, it should still be infeasible to brute force the passwords as long as you have selected a decent hash. 3) Even if it's possible to brute force a single password, it'll be absolutely impossible to brute force an entire database of them, so your customers as a whole as protected. – deceze Jan 11 '23 at 16:00
  • You may do some pre-hashing on the client if you absolutely never want to see the raw passwords, that's not unreasonable. But that pre-hash isn't the final word, the actual hashing happens on the server. – deceze Jan 11 '23 at 16:02
  • thanks @deceze this is all good advice! is there a more appropriate slow algorithm than SHA256? because the gap between performance on regular hardware and specialised is quite big – Anters Bear Jan 11 '23 at 16:05
  • 1
    bcrypt or scrypt are commonly used. SHA hashes are not appropriate, since they're explicitly designed to be *fast*. Hashes like the mentioned bcrypt are explicitly designed to be expensive and hard to optimise for in hardware. – deceze Jan 11 '23 at 16:07
  • The key to mitigate brute-forcing is **key-stretching**, all password-hashing functions like BCrypt, SCrypt and Argon2 have kind of a cost factor which controls the necessary time to calculate a single hash. – martinstoeckli Jan 13 '23 at 12:55

0 Answers0