php - PHP 中的 2 路字符串加密 - 其中哪个更安全?

标签 php security encryption pii

我需要通过查询字符串传递一个值以显示在我的页面上。该值必须加密,因为它包含一些供查看页面的用户使用的 PII。并且为了让用户可读,它需要在显示时能够被解密。
我正在使用 PHP,到目前为止的研究使我获得了 openssl_encryptopenssl_decrypt连同这两个代码资源:

  • https://bhoover.com/using-php-openssl_encrypt-openssl_decrypt-encrypt-decrypt-data/
  • https://gist.github.com/joashp/a1ae9cb30fa533f4ad94

  • 我很喜欢#1,因为iv实际上附加到返回的加密字符串。这让我们不是 必须存储一个 iv作为一个常量,并能够在每次调用函数时生成一个新的。这对我来说似乎比使用相同的 key 更安全和 iv每次。 真的吗?如果是这样,除了痛苦的显而易见的原因之外,我是否应该知道任何原因? .我不喜欢的是我认为连接 ivkey带有可以在其他潜在密码文本或 :: 中自然出现的字符/字符串(在本例中为 iv )成了问题。使用这种方法,在尝试加密 7000 多个电子邮件地址时,其中一半多一点的结果是这些奇怪的字符,���6CTΣ除其他外)在解码的字符串中,从而破坏了它。
    #2 很棒,因为它有效!!我还没有找到可以破坏它的字符串……尤其是在我的电子邮件列表中。但是如上所述,这需要。 ivkey 始终是相同的值并存储在某个变量中。这似乎是 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, 0, $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, 0, 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, $encrypt_method, $key, 0, $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 - 本质上没有银行业务或 super secret - 并允许他进行更新?它正在 PHP 中使用,看起来有点像:print "<li>Email: " . encrypt_decrypt('decrypt', my_sanitize_fxn($_GET['ue']) . "</li">;

  • 关于#3 的快速说明:
    我猜它会是 最好加密一些不是 PII 的东西(例如数据库中用户的唯一 ID)以通过查询字符串发送,然后解密该值并使用它通过数据库查询查找他的电子邮件地址。尽管我最终可能会在他结束时采取这种方式,但我们只能说目前正在发挥作用的因素(为此解释会使这个问题离主题太远)阻止它成为一个远程可行的选择。
    我认为了解我在这里得到的东西会很好地延续到最终的解决方案中。除了对我在整个过程中提出的一些正式问题的回答之外,我很想听听任何做得特别差或做得特别好的事情,或者只是一般性评论。
    预先感谢您愿意分享的任何智慧。

    最佳答案

    很抱歉我懒得把我的例子应用到你的代码中,但它不应该那么复杂,因为下面的代码是一个完整的示例
    具有随机 IV 的 AES GCM 256 字符串加密。将 IV 和标签添加到密文中,然后进行 Base64 编码。
    请注意,代码没有任何错误处理,仅用于教育目的!不要使用静态 key 进行加密。
    输出:

    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, 0, $ivlen);
        $tag = substr($c, $ivlen, $taglen=16);
        $ciphertext_raw = substr($c, $ivlen+$taglen);
        return openssl_decrypt($ciphertext_raw, 'aes-256-gcm', $encryptionKey, OPENSSL_RAW_DATA, $iv, $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;
    ?>
    

    关于php - PHP 中的 2 路字符串加密 - 其中哪个更安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64022615/

    相关文章:

    android - API 21生成 key 对时无法生成证书异常

    c# - 加密和解密密码

    node.js - 使用 Amazon KMS 加密值,使用 DynamoDB 和 Lambda (NodeJS) 存储/检索

    java - 解密加密的 .ser 文件并抛出 StreamCorruptedException

    php - 如何在 select 命令中选择表的主索引作为列

    php - Elasticsearch多个术语/过滤器

    ios - iOS 8,9 上的 NSUserDefaults 有多安全?

    security - 如何验证域凭据(来自 native 代码)?

    php - 等待连接 (netbeans-xdebug) MAMP OS X

    php - htaccess 使用完整的 url 重定向