代码之家  ›  专栏  ›  技术社区  ›  Corey Ballou

带base64编码和序列化的Mcrypt双向加密问题

  •  1
  • Corey Ballou  · 技术社区  · 15 年前

    更新(解决方案)

    由于这篇文章似乎得到了相当多的关注,我想让你知道,最终的解决办法是提供一个适当的 enctype 中的(内容类型)参数 <FORM> 宣言。必须将值设置为 multipart/form-data 防止使用默认的enctype进行编码 application/x-www-form-urlencoded . 下面的一小段摘录 Forms in HTML Documents 在W3.ORG:

    内容类型 “application/x-www-form-urlencoded”是 发送大型邮件时效率低下 二进制数据或文本的数量 包含非ASCII字符。这个 内容类型“多部分/表单数据” 应用于提交表单 包含文件、非ASCII数据, 和二进制数据。

    以下是正确的表格声明:

    <FORM method="POST" action="/path/to/file/" name="encryptedForm" enctype="multipart/form-data">
    

    初始问题

    我正在研究一个表单垃圾邮件保护类,它基本上使用mcrypt将表单字段名替换为加密值。问题在于,mcrypt加密不仅限于字母数字字符,而且会使表单字段无效。 考虑到下面的代码,您能想到我在解密已经加密的数组的值时遇到问题的任何原因吗?

    /**
     * Two way encryption function to encrypt/decrypt keys with
     * the DES encryption algorithm.
     */
    public static function encryption($text, $encrypt = true)
    {
        $encrypted_data = '';
        $td = mcrypt_module_open('des', '', 'ecb', '');
        $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        if (mcrypt_generic_init($td, substr(self::$randomizer, 16, 8), $iv) != -1) {
            if ($encrypt) {
                // attempt to sanitize encryption for use as a form element name
                $encrypted_data = mcrypt_generic($td, $text);
                $encrypted_data = base64_encode($encrypted_data);
                $encrypted_data = 'i' . strtr($encrypted_data, '+/=', '-_.');
                self::$encrypted[] = $encrypted_data;
            } else {
                // reverse form element name sanitization and decrypt
                $text = substr($text, 1);
                $text = strtr($text, '-_.', '+/=');
                $text = base64_decode($text);
                $encrypted_data = mdecrypt_generic($td, $text);
            }
            mcrypt_generic_deinit($td);
            mcrypt_module_close($td);
        }
        return $encrypted_data;
    }
    

    稍后,我会使用以下方法调用设置隐藏表单元素的值:

    base64_encode(serialize(self::$encrypted))
    

    本质上,隐藏字段包含一个用其加密值加密的所有表单字段的数组。所以我知道哪些字段需要在后端解密。提交表单时,此字段将在后端用以下代码进行分析:

        // load the mapping entry
        $encrypted_fields = $input->post('encrypted', '');
        if (empty($encrypted_fields)) {
            throw new AppException('The encrypted form field was empty.');
        }
    
        // decompress array of encrypted fields
        $encrypted_fields = @unserialize(base64_decode($encrypted_fields));
        if ($encrypted_fields === false) {
            throw new AppException('The encrypted form field was not valid.');
        }
    
        // get the mapping of encrypted keys to key
        $data = array();
        foreach ($_POST as $key => $val) {
            // if the key is encrypted, add to data array decrypted
            if (in_array($key, $encrypted_fields)) {
                $decrypted = self::encryption($key, false);
                $data[$decrypted] = $val;
                unset($_POST[$key]);
            } else {
                $data[$key] = $val;
            }
        }
    
        // merge $_POST array with decrypted key array
        $_POST += $data;
    

    我试图解密加密的表单域密钥失败。它只是在 $_POST 数组。我想也是这样 base64_encoding serialization 正在从中剥离字符 $encrypted_data . 是否有人可以验证这是否是罪魁祸首,以及是否有其他方法来编码表单密钥?

    1 回复  |  直到 9 年前
        1
  •  1
  •   Chris Gutierrez    15 年前

    所以我取了您的代码,并对其进行了一些修改,以便删除post请求的元素,并且您的函数似乎可以正常工作。如果您使用我发布的代码并用它创建一个脚本,它应该在CLI中运行,您将看到它正确地加密/解密字段。这就意味着post请求会造成加密/序列化/编码数据的混乱。如果使用框架,我将更深入地研究它如何处理post数组,因为它可能改变您的键/值,导致它们不匹配。您发布的代码似乎很好。

    <?php
        /**
         * Two way encryption function to encrypt/decrypt keys with
         * the DES encryption algorithm.
         */
        function encryption($text, $encrypt = true, &$encryptedFields = array())
        {
            $encrypted_data = '';
            $td = mcrypt_module_open('des', '', 'ecb', '');
            $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
            if (mcrypt_generic_init($td, substr('sdf234d45)()*5gf512/?>:LPIJ*&U%&^%NBVFYUT^5hfhgvkjtIUUYRYT', 16, 8), $iv) != -1) {
                if ($encrypt) {
                    // attempt to sanitize encryption for use as a form element name
                    $encrypted_data = mcrypt_generic($td, $text);
                    $encrypted_data = base64_encode($encrypted_data);
                    $encrypted_data = 'i' . strtr($encrypted_data, '+/=', '-_.');
                    //self::$encrypted[] = $encrypted_data;
                    $encryptedFields[] = $encrypted_data;
                } else {
                    // reverse form element name sanitization and decrypt
                    $text = substr($text, 1);
                    $text = strtr($text, '-_.', '+/=');
                    $text = base64_decode($text);
                    $encrypted_data = mdecrypt_generic($td, $text);
                }
                mcrypt_generic_deinit($td);
                mcrypt_module_close($td);
            }
            return $encrypted_data;
        }
    
        $encryptedFields = array();
    
        // encrypt some form fields
        encryption('firstname', true, $encryptedFields);
        encryption('lastname', true, $encryptedFields);
        encryption('email_fields', true, $encryptedFields);
    
        echo "Encrypted field names:\n";
        print_r($encryptedFields);
    
        // create a usable string of the encrypted form fields
        $hiddenFieldStr = base64_encode(serialize($encryptedFields));
    
        echo "\n\nFull string for hidden field: \n";
        echo $hiddenFieldStr . "\n\n";
    
    
        $encPostFields = unserialize(base64_decode($hiddenFieldStr));
    
        echo "\n\nDecrypted field names:\n";
        foreach($encPostFields as $field)
        {
            echo encryption($field, false)."\n";
        }
        ?>