Use of Initialization Vector in openssl_encrypt

2020-01-27 11:15发布

问题:

I had a look at this question, and wanted to do it for myself. When I ran this code (taken straight from this answer):

$textToEncrypt = "My super secret information.";
$encryptionMethod = "AES-256-CBC";  // AES is used by the U.S. gov't to encrypt top secret documents.
$secretHash = "25c6c7ff35b9979b151f2136cd13b0ff";

//To encrypt
$encryptedMessage = openssl_encrypt($textToEncrypt, $encryptionMethod, $secretHash, '1234567812345678');

//To Decrypt
$decryptedMessage = openssl_decrypt($encryptedMessage, $encryptionMethod, $secretHash);

//Result
echo "Encrypted: $encryptedMessage <br>Decrypted: $decryptedMessage";

However I get the warning

openssl_encrypt(): Using an empty Initialization Vector (iv) is potentially insecure and not recommended

So I went and had a look at the docs, but there 'is no documentation'. I found this comment, but still no mention of what the Initialization Vector should be and how I should use it. Can anyone enlighten me?

I know I could have done some more Googleing, but Stackoverflow comes up first in so many search results I thought this question might be useful to anyone else who was having this problem.

回答1:

An IV is generally a random number that guarantees the encrypted text is unique.

To explain why it's needed, let's pretend we have a database of people's names encrypted with the key 'secret' and no IV.

1 John dsfa9p8y098hasdf
2 Paul po43pokdfgpo3k4y
3 John dsfa9p8y098hasdf

If John 1 knows his cipher text (dsfa9p8y098hasdf) and has access to the other cipher texts, he can easily find other people named John.

Now in actuality, an encryption mode that requires an IV will always use one. If you don't specify an IV, it's automatically set to a bunch of null bytes. Imagine the first example but with a constant IV (00000000).

1 John dsfa9p8y098hasdf 00000000
2 Paul po43pokdfgpo3k4y 00000000
3 John dsfa9p8y098hasdf 00000000

To prevent repeated cipher texts, we can encrypt the names using the same 'secret' key and random IV's:

1 John sdf875n90mh28458 45gh3546
2 Paul fg9087n5b60987nf 56897ngq
3 John gjhn0m89456vnler 8907345f

As you can see, the two 'John' cipher texts are now different. Each IV is unique and has influenced the encryption process making the end result unique as well. John 1 now has no idea what user 3's name is.

Decryption requires the use of the same IV the text was encrypted with of course, which is why it must be stored in the database. The IV is of no use without the key so transmitting or storing it with the encrypted text is of no concern.

This is an overly simplistic example, but the truth is, not using IV's has serious security ramifications.


Now your code appears to be setting the IV (1234567812345678) but not using it on decryption. That's certain to fail.

You also may want to utilize some of PHP's IV generation functions. I think this should work for you:

$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$encryptedMessage = openssl_encrypt($textToEncrypt, $encryptionMethod, $secretHash, 0, $iv);
$decryptedMessage = openssl_decrypt($encryptedMessage, $encryptionMethod, $secretHash, 0, $iv);

For storage/transmission, you can simply concatenate the IV and cipher text like so:

$data = $iv.$encryptedMessage;

Then on retrieval, pull the IV out for decryption:

$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = substr($data, 0, $iv_size);
$decryptedMessage = openssl_decrypt(substr($data, $iv_size), $encryptionMethod, $secretHash, 0, $iv);

For more info, check out PHP's Mcrypt library. It's quite full featured and has tons of examples, many of which can help you out with openssh encryption implementations. http://php.net/manual/en/function.mcrypt-encrypt.php