devise icon indicating copy to clipboard operation
devise copied to clipboard

Passwords shouldn't be limited to 72 characters

Open rugk opened this issue 5 years ago • 7 comments

Current behavior

Due to bcrypt passwords are limited to 72 bytes/characters.

Expected behavior

Possibly pre-hash the password before putting it into bcrypt. See downstream issue https://github.com/tootsuite/mastodon/issues/13152 for details why it is a problem in Mastodon.

Note: I have no knowledge of Ruby, I'm just creating this issue.

rugk avatar Nov 05 '20 18:11 rugk

72 Bytes actually. UTF-8 character encodes into 1~4 bytes

tribela avatar Mar 03 '21 02:03 tribela

Maximum password length should be limited because of computation time of password hash. It takes 15-150 seconds of uninterruptible bcrypt hash calculation if password have ~1024 characters length.

drakmail avatar Dec 08 '21 12:12 drakmail

With https://github.com/heartcombo/devise-encryptable you can use other encryption tools, such as argon2, which will hash up to a 4Gb input string (not recommended, it might let out the magic smoke!).

pboling avatar Jan 24 '22 19:01 pboling

Anyone who is (or is considering) pre-hashing should be aware that OWASP considers it a dangerous practice that should be avoided.

xanderificnl avatar Feb 05 '22 02:02 xanderificnl

@drakmail The bcrypt algorithm actually ignores the characters after the first 72 bytes, so there is no way to hash a 1024 character password with bcrypt. Perhaps you meant another algorithm?

pboling avatar Feb 14 '22 21:02 pboling

@pboling when user sends a long password (tens of thousands characters) the rails server hangs for several seconds consuming 100% CPU

drakmail avatar Feb 15 '22 05:02 drakmail

@pboling when user sends a long password (tens of thousands characters) the rails server hangs for several seconds consuming 100% CPU

Are you using the bcrypt-ruby gem? That's the default in Rails' Gemfile. Starting at 512 bytes it should raise BCrypt::Errors::InvalidHash, for instance:

3.1.0 (mbb)[1] » BCrypt::Password.create('a'*512)
BCrypt::Errors::InvalidHash: invalid hash

So bcrypt shouldn't return a hash for a password containing 1024 characters. The hang-up you're describing is most likely related to ruby itself, in my IRB console, this does cause a (temporary) hang up:

# only do this if you don't mind hangups. 
3.1.0 (mbb)[2] » 'a'*99999999999999

Back to the main point; the limit of bcrypt is 72 bytes, which effectively means a password containing only a's of 72 bytes is equal to 73 bytes of a's ad infinitum. To illustrate this:

3.1.0 (mbb)[3] » b72 = BCrypt::Password.create('a'*72) # 72 bytes of a's
=> "$2a$12$g2IH91mSXl6Xww/RfvVI6.l3g9fHL.KtD5Xg4bhoVE5l6cx3hQRrC"
3.1.0 (mbb)[4] » b72 == 'a'*73 # <-- not the same password
=> true
3.1.0 (mbb)[5] » b72 == 'a'*74 # <-- not the same password
=> true
3.1.0 (mbb)[6] » b72 == 'a'*100 # <-- not the same password
=> true

In the linked thread in a mastodon repository, a case is made for seemingly small passwords exceeding 72 bytes. It's a valid case and point. Truncating passwords should be avoided. So, in case anyone also hit this issue, here are a couple of ways to handle this issue:

  • You can refuse passwords longer than 72 bytes. This isn't a great way.
  • Use a different algorithm, i.e. Argon2i or Argon2id.
  • Use BCrypt for passwords up to 72 bytes and switch over to Argon2i(d) for passwords longer. (bcrypt has been around way longer than argon2 and has proven itself; so thats something to keep in mind.)

In any case, password fields should have a limit to avoid bad actors from DoS'ing your service and this limit should be enforced before you hash the password. Especially when using a slow algorithm (i.e. bcrypt, argon, etc.)

xanderificnl avatar Feb 15 '22 14:02 xanderificnl