问题描述
我需要通过查询字符串传递值以显示在页面上。该值必须加密,因为它包含一些供用户查看页面的PII。并且为了使用户可读,它需要能够在显示时被解密。
到目前为止,我一直在使用PHP,研究使我进入openssl_encrypt
和openssl_decrypt
以及以下两个代码资源:
- https://bhoover.com/using-php-openssl_encrypt-openssl_decrypt-encrypt-decrypt-data/
- https://gist.github.com/joashp/a1ae9cb30fa533f4ad94
我非常喜欢#1,因为iv
实际上附加到返回的加密字符串上。这样,我们不需要就不必将iv
存储为常量,并且每次调用该函数时都可以生成一个新常量。与每次使用相同的key
和iv
相比,这对我来说似乎更安全。 是真的吗?如果是这样,我是否出于某种原因而不是痛苦的显而易见?。我不喜欢的是我认为将iv
和key
与可以找到的字符/字符串(在这种情况下为::
)连接在一起在其他潜在的密文或iv
中自然出现的问题变得很棘手。使用这种方法,在尝试对7000多个电子邮件地址进行加密时,其中超过一半的地址以解码字符串中的这些奇怪字符(���6CTΣ
等)结尾,从而将其破坏。
#2很棒,因为它有效!!我还没有找到一个可以打破它的字符串...尤其是在我的电子邮件列表中。如上所述,这是 But 。 iv
和key
始终是相同的值,并存储在某个位置的变量中。这似乎是1个微小维护问题,但更多是安全问题。
所以我做了一些阅读/思考,然后with this working example-这是代码:
<?PHP
// generate key with base64_encode(openssl_random_pseudo_bytes(32);
// and save it in a constant.
define('ENCRYPT_KEY_1','CuH8WPfXzMj0xRWybHjssWJ+IhTDqL5w0OD9+zXFloA=');
function encrypt_decrypt($action,$string) {
$output = false;
$encrypt_method = "AES-256-CBC";
$key = ENCRYPT_KEY_1;
$ivLen = openssl_cipher_iv_length($encrypt_method);
/**
* the key was generated with 32 pseudo-bytes and then base64Encod()-ed.
* Not sure of the reason for encoding - just decoding in case it's necessary.
*
* Thoughts?
* **/
$key = base64_decode($key);
if ( $action == 'encrypt' ) {
/**
* "AES-256-CBC" expects a 16-byte string - create an 8-byte string to be
* converted to a 16-byte hex before being used as the initialization vector
* TLDR" in order to end up with 16-bytes to Feed to openssl_random_pseudo_bytes,* divide initial length in half as the hex value will double it
* */
$iv = openssl_random_pseudo_bytes($ivLen/2);
$iv = bin2hex($iv);
$tmp_data_str_in = openssl_encrypt($string,$encrypt_method,$key,$iv);
$output = base64_encode($tmp_data_str_in . $iv);
} else if( $action == 'decrypt' ) {
$data_str_in = base64_decode($string);
// This time,rather than generating one,we get the iv (converted to hex)
// by grabbing the last 16 characters of the concatenation of the 2 that happened
// during encryption.
$iv = substr($data_str_in,-$ivLen);
// And Now we just grab everything BUT the last 16 characters. We'll
// run openssl_decrypt and return a copy of the original input
$encrypted_txt_str = substr($data_str_in,strlen($data_str_in)-$ivLen);
// Notice we are returning the encrypted value of a string consisting of
// encoded versions of the input text and a random `IV` - we'll grab the `IV`
// from it later in order to decrypt later.
$output = openssl_decrypt($encrypted_txt_str,$iv);
}
return $output;
}
$plain_txt = "memyselfandi@i.me";
echo "Plain Text = " .$plain_txt. "\n";
$encrypted_txt = encrypt_decrypt('encrypt',$plain_txt);
echo "Encrypted Text = " .$encrypted_txt. "\n";
$decrypted_txt = encrypt_decrypt('decrypt',$encrypted_txt);
echo "Decrypted Text = " .$decrypted_txt. "\n";
if ( $plain_txt === $decrypted_txt ) echo "SUCCESS";
else echo "Failed";
echo "\n";
?>
所以我想我的主要问题是:
- 我是否正确地认为,使用在执行函数时生成的动态
iv
的解决方案比预先定义好静态iv
并用于每次加密的解决方案更安全?如果没有,我想念什么? - (可能成功的)攻击有哪些开放空间,以便这些方法中的任何/所有方法都能暴露出来?如何修复或修改它们以减轻风险
- 在显示用户PII的网站上的生产环境中是否可以接受这些方法中的任何一种(希望我将它们放在一起)?本质上没有银行或超级秘密信息,并且允许他进行更新?它正在PHP中使用,外观类似于:
print "<li>Email: " . encrypt_decrypt('decrypt',my_sanitize_fxn($_GET['ue']) . "</li">;
关于#3的快速注释:
我猜想 FAR 最好加密IS N'T PII的某些内容(例如数据库中用户的唯一ID)以通过查询字符串发送,然后解密该值并使用它来通过数据库查询查找他的电子邮件地址。尽管最后我可能会这样说,但请说一下目前正在发挥作用的一些因素(对此进行解释会使该问题偏离主题),这使得它甚至无法遥不可及可行的选择。
我认为了解我在这里所学的知识将很好地延续到最终解决方案中。除了回答我在整个过程中提出的一些正式问题之外,我很想听听做得特别差或做得好或只是一般性评论的事情。
在此先感谢您分享的智慧。
解决方法
很抱歉懒于在代码中采用我的示例,但是它应该没有那么复杂,因为以下代码是一个完整示例, 带有随机IV的AES GCM 256字符串加密。 IV和标记被放在密文之前,然后进行Base64编码。
请注意,该代码没有任何错误处理,仅用于教育目的!请勿使用静态密钥进行加密。
输出:
Sample AES GCM 256 string encryption
Please note that this code does not have any error handling and is for educational purpose only
Do NOT use static keys for encryption !
plaintext: The quick brown fox jumps over the lazy dog
encrypt: jemvFuwhIaUYx49d1nap6uKz8wMIorvQuRD/PGt+SYhFt8iaK1fiqAf8CjWtVNYqFZATStgq2XQuUAhbnhMtpzHDPN7oUFo=
decrypt: The quick brown fox jumps over the lazy dog
代码:
<?php
function encrypt($encryptionKey,$data) {
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-gcm'));
$encrypted = openssl_encrypt($data,'aes-256-gcm',$encryptionKey,OPENSSL_RAW_DATA,$iv,$tag);
return base64_encode($iv . $tag . $encrypted);
}
function decrypt($encryptionKey,$data) {
$c = base64_decode($data);
$ivlen = openssl_cipher_iv_length($cipher="AES-256-GCM");
$iv = substr($c,$ivlen);
$tag = substr($c,$ivlen,$taglen=16);
$ciphertext_raw = substr($c,$ivlen+$taglen);
return openssl_decrypt($ciphertext_raw,$tag);
}
echo 'Sample AES GCM 256 string encryption' . PHP_EOL;
echo 'Please note that this code does not have any error handling and is for educational purpose only' . PHP_EOL;
echo 'Do NOT use static keys for encryption !'. PHP_EOL . PHP_EOL;
$plaintext = 'The quick brown fox jumps over the lazy dog';
$key = '12345678901234567890123456789012'; // 32 bytes = 256 bit key
echo 'plaintext: ' . $plaintext .PHP_EOL;
$encrypt = encrypt($key,$plaintext);
echo 'encrypt: ' . $encrypt . PHP_EOL;
$decrypt = decrypt($key,$encrypt);
echo 'decrypt: ' . $decrypt . PHP_EOL;
?>