mcrypt 2路加密问题与base64编码和序列化

发布于 2024-08-17 03:19:03 字数 3350 浏览 10 评论 0原文

更新(解决方案)

由于这篇文章似乎得到了相当多的关注,我想让您知道解决方案最终是在 enctype (内容类型)参数>

声明。您必须将该值设置为 multipart/form-data 以防止使用 application/x-www-form-urlencoded 的默认 enctype 进行编码。下面摘录自HTML 文档中的表单 在 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))

本质上是隐藏的field 包含所有表单字段的数组,这些字段均使用其加密值进行加密。这样我就知道哪些字段需要在后端解密。提交表单后,该字段将在后端使用以下代码进行解析:

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

UPDATE (SOLUTION)

Since this post seems to get decent amount of attention, I'd like to let you know that the solution ended up being to provide a proper enctype (content type) parameter in the <FORM> declaration. You must set the value to multipart/form-data to prevent encoding that would otherwise take place using the default enctype of application/x-www-form-urlencoded. A small excerpt below from Forms in HTML Documents at w3.org:

The content type
"application/x-www-form-urlencoded" is
inefficient for sending large
quantities of binary data or text
containing non-ASCII characters. The
content type "multipart/form-data"
should be used for submitting forms
that contain files, non-ASCII data,
and binary data.

And here is the proper FORM declaration:

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

INITIAL QUESTION

I am working on a form spam protection class which essentially replaces form field names with an encrypted value using mcrypt. The problem with this is that mcrypt encryption is not limited to only alphanumeric characters which would invalidate form fields. Given the code below, can you think of any reason why I'd be having problems decrypting the values of the already encrypted array?

/**
 * 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;
}

I later make a call setting a hidden form element's value using:

base64_encode(serialize(self::$encrypted))

Essentially the hidden field contains an array of all form fields which were encrypted with their encrypted value. This is so I know which fields need to be decrypted on the backend. Upon form submission this field gets parsed on the backend with the following code:

    // 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;

My attempts to decrypt the encrypted form field keys are failing. It's simply creating a new garbled key in the $_POST array. My guess is that either base64_encoding or serialization is stripping chars from the $encrypted_data. Could somebody verify if this is the culprit and whether there are any alternative methods for encoding form keys?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

琉璃梦幻 2024-08-24 03:19:03

所以我拿了你的代码,并对其进行了一些修改,以便我可以删除发布请求的元素,并且你的函数似乎工作正常。如果您采用我发布的代码并用它创建一个脚本,它应该在 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";
    }
    ?>

So I took your code, and modified it a little so that I can remove the element of a post request and your function seems to work fine. If you take the code I posted and create a script with it, it should run in the cli and you'll see its encrypting/decrypting the fields correctly. This would have to mean that the post request is some how garbling the encrypted/serialized/encoded data. If using a framework, I would look more into how it handles the post array as it could altering your keys/values causing them to not match up. The code that you posted seems fine.

<?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";
    }
    ?>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文