在 openssl_crypt 中使用初始向量

我看了一下 这个的问题,想自己来做。当我运行这段代码时(直接取自 这个答案) :

$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";

然而我得到了警告

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

所以我去看了一下 医生,但是“没有文档”。我找到了这个 ABc1,但是仍然没有提到初始向量应该是什么以及我应该如何使用它。有人能告诉我吗?

我知道我本可以做更多的谷歌搜索,但 Stackoverflow 出现在如此多的搜索结果中,我认为这个问题可能对其他有这个问题的人有用。

67922 次浏览

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 86714561
2 Paul fg9087n5b60987nf 13541814
3 John gjhn0m89456vnler 44189122

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 along side the encrypted data. For decryption the IV is useless‡ without the key though 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, which is the reason IV's exist in the first place. Undoubtedly, countless IV-less encryption implementations have been exploited.

‡ As my security knowledge has progressed, I've learned that there are some more complicated exploits for various block cipher modes and concatenation techniques (not particularly related to the concatenation methodology described below) that can utilize IV's to attain keys. Notable examples exploit RC4 data streams to determine the keys and decrypt wi-fi traffic for older technologies like WEP and WPA:


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, one option is to 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);

If you're storing the IV in a database for example, you could also store the IV in an adjacent column to simplify the extraction process.


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


An obligatory security disclaimer: My words describe the simplest of simple concepts in the simplest possible way. In the reach and depth of my knowledge, I am an absolute novice. Even the best of the best security researchers and experts introduce encryption vulnerabilities all the time. Even so, the less you write on your own, the better off your users, customers, family, and friends will be. No offense! While it's fun, interesting, and even practical to learn about encryption theory, especially applied to computer science in something as accessible as PHP, staying up to date with best practices and using the latest trusted libraries is going to provide the most secure systems for 99.9999999% of us. Nothing is perfect, but it's best to stand on the shoulders of giants. As a wise man probably might have said, "The more you learn the more you realize how little you know."