4

From my understanding the IV is used as a previous block for the first block only. But since each block is used by the following this adds significant security is a IV is provided and not based on the password.

Thus I implemented a login system when the user provides both, distinct with a minimum of 8 characters using any characters they care as long they use at least 3 types (upper, lower, numeric, special, non ASCII).

But now someone told me that by defining the IV I weaken the encryption as that would make the password sort-of useless.

I did not exactly understand his explanation.

My code looks like this:

$hash = openssl_encrypt($username, 'aes-256-cbc', $password, false, md5($iv, true));
  • Yes I am encrypting the username
  • Both the password and IV are user defined
  • Yes I am asking them for two passwords
  • I'm wrapping the IV in RAW md5 to ensure I get a standard 16 chr length

Can someone clarify if the IV use in this manner is a good practice and is this idea is strong enough.

transilvlad
  • 141
  • 1
  • 4
  • Why force the user to generate a suitable IV when you can do it yourself and prepend it to the ciphertext? – Navin Mar 23 '16 at 04:43
  • Instead of rolling your own, might I recommend [using a well-studied PHP encryption library](https://paragonie.com/blog/2015/11/choosing-right-cryptography-library-for-your-php-project-guide)? > `$hash = openssl_encrypt($username, 'aes-256-cbc', $password, false, md5($iv, true));` This isn't a hash. – Scott Arciszewski Apr 08 '16 at 16:05

2 Answers2

20

My suspicion is: you're doing it wrong™.

This is why I think so, from the top of my head:

  1. The variable holding the ciphertext is called hash.
  2. You are encrypting the user name.
  3. This looks like an XY-Problem.
  4. You do not (show how you) derive the key from the password (the openssl_encrypt password is the key, according to the docs. So unless you are deriving a key from the user supplied password, that's not going to work out.)
  5. You are not using a CSPRNG to get an IV for AES-CBC.

But now someone told me that by defining the IV I weaken the encryption as that would make the password sort-of useless.

That someone was right. See the linked question on crypto about using a CSPRNG with AES-CBC to learn more.

Tobi Nary
  • 14,302
  • 8
  • 43
  • 58
10

First off, the usual warning: Do not roll your own crypto, chances are you're getting it wrong.

Now the second warning: You seem to encrypt data solely using AES-CBC, which is very dangerous, you really should use AES-GCM, as the CBC mode of operation for AES doesn't prevent the message from being modified, possibly leading to undesirable results.

Now the big issue with your encryption scheme: You don't introduce external randomness.
The CBC mode requires an IV that is unpredictable or otherwise you're susceptible to attacks. By the fact that you use MD5(pw2), this IV is not unpredictable as most passwords will be predictable. Even worse, the IV is supposed to introduce randomness into the encryption and make the same plaintext get encrypted to different ciphertexts. Chances are however that your user won't understand that and will re-use his pair of passwords across multiple encryptions and therefore you're susceptible to static IV attacks.

Another few minor issues:

  1. You don't ensure the key (aka the $password) is exactly 32 bytes long (a requirement for using AES-256) and (pseudo-)random (a formal requirement for security), you'll probably run into problems once users pick passwords with more than 32 characters.
  2. You ask the user for two passwords while in fact you could just ask them for one and derive the two from that (deterministically).
  3. You don't process the password. Right now, if a ciphertext gets leaked, I could just go ahead and try to brute-force the user's password, because you deployed no counter-measures to password guessing (like using bcrypt).

So, how to fix it?

  1. Use AES-GCM with a 12-byte nonce.
  2. Use OpenSSLs RNG to generate said nonce.
  3. Derive the AES-GCM key from the users password securely (i.e. using bcrypt or Argon2)
SEJPM
  • 9,500
  • 5
  • 35
  • 66