Brute Force Attacks On Browser based Secure Remote Password Protocol
The first thing you can do is to implement the recommendations on the thinbus page:
- Use symmetric AES encryption with a key only visible at the webserver to encrypt the verifier v value within the database. This protects against off-site database backups being used in an offline dictionary attack against v.
The thinbus spring demo encrypts the verifier using some framework code that was well documented. I didn’t have to do anything much to implement transparent AES encryption at the application layer. We would hope that every popular language has a helpful library to encrypt data into a column within the application data access code. If someone would like to send me a PR to add that feature to the PHP demo I would be happy to review and merge.
Note that if you encrypt a database column you cannot backup the encryption key to the same location as the database backups. So it is not unknown for people to have database backups after a disaster but to have lost the encryption key. Obviously dear reader you and I are good at our jobs so that would never happened to us. Yet if it did the worst outcome would be that your customers would have to do a password reset. (Nope it’s never happened to me!)
When it comes to password strength meters they are an art. You can also trivially get hold of a 4G file of common passwords off the net and also rainbow tables of hashed password lookups. Serious deployments could use both an algorithmic based password strength checker at the browser and also pass a hash of the password to the server along with the new verifier. The server can check the password hash before accepting the verifier. If the password hash matches the hash of a password known to every password guessing bot script on the Internet the server can reject the verifier. If not you can store the only the verifier confident that it’s was created with a strong user password.
The next area to address is online dictionary attacks. It is common practice to use an expensive hash function to slow down brute force attacks. Yet this is a bad idea for browser authentication. To recap the original SRP design uses a one way hash `H`. Thinbus now defaults to SHA256† as it is both fast and secure enough for signing purposes. But fast is bad, right? Huh?
Well the perceived issue with fast hashing is that it’s quick to brute force. If you are salting a password in a database with a one way hash you are strongly encouraged to use a slow hash such as bcrypt, scrypt or pbkdf2. But if we use those slow hash functions with with SRP we have to run it both at the browser and on the server for every authentication. You open yourself up to a denial of service attack. Someone trying to brute force you online will raise the CPU and memory on your server. That costs you money and could make your service unusable for your real users. Also to slow down a GPU you need a really expensive hash and that might not be responsive in all mobile phone browsers. It’s much better to find another solution folks.
As Sébastien Pasche pointed out to me what we want is an asymmetrical algorithm to slow down an attacker. BitCoin uses hashcash to make work expensive but the verification of work cheap. This asymmetrical technique can be used in conjunction with SRP. Require the browser to submit a hashcash proof-of-work in addition to the SRP password proof. For example require that the browser provide a string along with the password proof that when hashed together with
B gives some number of leading zero bits. As that server public value is constructed using cryptography strong random numbers the proof-of-work cannot be pre-computed.
Of course independently of authentication someone might launch a DDoS against your site. You can use a CDN provider who will absorb such attacks for you. In which case they may provide a throttling service which would also protect against brute force password guessing using a GPU.
The final thing to consider with password guessing is a sneaky targetted attack. Someone using a botnet to guess a single users passwords using, say, combinations of every word the individual ever wrote on social media. That could guess combinations of children’s names and high-school details etc. That can be done by many zombie machines guessing slowly that add up to a lot of guesses. As the traffic comes from many locations it may fly below the radar that is looking to block attack bots. This attack is easy to foil. After five failed attempts by any user you can put up an “are you human” check just for that user. After 50 failed attempts suspended the user for five minutes. After 100 suspend for an hour. That makes for a denial of service attack against a specific user possible. They would probably thank-you for suspending their account rather than let a sneaky botnet attack hijack it.
The approach of suspending accounts on too many guesses counters many of the guessing attacks that the proof-of-work and password strength verifier would counter. The proof-of-work combined with these makes it more expensive to launch an attack design to suspend many users with bad guess as a denial of service attack.
All of the above basically says to have layers to the security with each layer rising the bar an attacker has to get over. It also promotes the “Unix Way” of composing many things that “do one thing well” that in combination cover a lot of ground. You should always use HTTPS and also consider 2FA combined with SRP for admin users. The more security features and defence layers the better.
† Note that using SHA256 not SHA1 deviates from the RFCs on SRP. This is because SHA1 isn’t considered strong any more. Using the RFC would be a lot better than sending the password to the server but since it is as easy to use SHA256 as SHA1 and mobile browsers are fast today I have opted for the newer standard. If you ever write an RFC remember to future proof it by making things like the hash algorithm negotiable!