【问题标题】:mcrypt 2 way encryption issue with base64 encoding and serializationmcrypt 2 路加密问题与 base64 编码和序列化
【发布时间】:2011-01-02 15:31:04
【问题描述】:

更新(解决方案)

由于这篇文章似乎得到了相当多的关注,我想告诉您解决方案最终是在<FORM> 声明中提供适当的enctype(内容类型)参数。您必须将该值设置为multipart/form-data,以防止使用默认编码类型application/x-www-form-urlencoded 进行编码。下面是来自 w3.org 的 Forms in HTML Documents 的一小段摘录:

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

这是正确的 FORM 声明:

<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_encodingserialization 正在从$encrypted_data 中剥离字符。 有人可以验证这是否是罪魁祸首以及是否有任何替代方法来编码表单键?

【问题讨论】:

  • @cballou 我也遇到了同样的问题 - 您是否发现导致它的帖子数组(如下)是什么原因?
  • @adam - 解决方法是确保您的表单具有 enctype="multipart/form-data" 因为这可以确保字符不会被编码和替换。
  • 谢谢 cballou。我使用 curl 发布但没有使用 base64_encode - 基本上是相同的修复!
  • 如果您有兴趣,我在jqueryin.com/2010/04/26/… 使用这种加密方法开源了我的垃圾邮件防护类 SPF30

标签: php forms encryption mcrypt


【解决方案1】:

所以我使用了你的代码,并对其进行了一些修改,以便我可以删除发布请求的元素,并且你的函数似乎工作正常。如果您使用我发布的代码并使用它创建一个脚本,它应该在 cli 中运行,您将看到它正确地加密/解密字段。这必须意味着发布请求对加密/序列化/编码数据有一些乱码。如果使用框架,我会更多地研究它如何处理 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";
    }
    ?>

【讨论】:

  • 值得注意的是,我的加密机制在尝试解密表单中的 POST 数据时不起作用,因为默认情况下,POST 数据会被编码。您必须设置&lt;form enctype="multipart/form-data"&gt;,因为这样可以确保字符不会被编码和替换。
猜你喜欢
  • 2010-12-29
  • 2013-09-25
  • 2013-05-10
  • 1970-01-01
  • 2011-07-22
  • 2018-03-13
  • 1970-01-01
  • 2016-07-13
  • 2010-10-09
相关资源
最近更新 更多