I'm working on a site that has to have compatibility with older versions of PHP, which suffer from a high-bit issue in the Blowfish crypt() implementation. Essentially, non-ASCII characters are not processed properly, and it may be possible for an attacker to reduce the brute-force space. There's a fix in 5.3.7, but I'll need to support earlier versions.

In order to work around the issue, I'm thinking of Base64 encoding the password prior to running it through crypt(). The idea is that base64_encode() is binary safe, and the output will never contain any characters whose most significant bit is set.

Is this a safe assumption, or should I look at an alternative method?

  • It'll shorten the maximal password length, but should work. If you want to avoid that, you can hash with a normal hash before feeding it to bcrypt. – CodesInChaos Feb 5 '13 at 16:29

Base64 output is limited to ASCII digits, letters (lowercase and uppercase), '+', '/', '=' and newline endings (CR and LF). All of these fit in the lower 7 bits.

Using Base64 encoding has the unfortunate side-effect of enlarging the data by about +33%. Since bcrypt has a size limit of 55 characters, so with Base64 encoding you are down to about 39 bytes.

Also, non-ASCII password characters open up the murky business of code pages (Unicode, UTF-8, Windows-1252...) and Base64 does not solve that; it just pushes the problem under the rug.

  • Any suggestions for better mitigations? – Polynomial Feb 5 '13 at 16:38
  • I suggest rejecting passwords with non-ASCII characters at registration time, and rejecting them again if submitted for a logon. – Thomas Pornin Feb 5 '13 at 16:40
  • Good idea. I'll make sure to do that. – Polynomial Feb 5 '13 at 16:47
  • Done. Final implementation loops through all chars (single-byte) and checks that they're 0x7F or lower, and rejects any string that violates this. – Polynomial Feb 5 '13 at 16:57
  • 2
    @Gilles Unfortunately reality gets in the way of such luxuries. – Polynomial Feb 5 '13 at 19:05

Just use effectivePass = Base64Encode(Sha256(Utf8Encode(password)))). Takes care of encoding and length limit. Then put effectivePass into bcrypt.

I believe for php treats strings are raw bytes, so you probably need to set some char-encoding http-header to UTF-8 instead of encoding.

  • The downside of this is that SHA-256 requires mcrypt support, which a lot of servers won't have. I can use SHA-1, but that makes me a bit nervous... would I need to worry about length extension attacks? – Polynomial Feb 5 '13 at 18:38
  • 1
    I can't think of any practical problem with SHA-1 or even MD5 in this context. Neither length extensions nor collisions are relevant. You probably could get away with CRC-128 or something like that. The only downside is PR, with people screaming "SHA-1 is borked11!1", just like they do it now with MD5. – CodesInChaos Feb 5 '13 at 20:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.