添加 MCrypt 的 AES-CBC 加密后数据未保存到数据库或未正确解密

Posted

技术标签:

【中文标题】添加 MCrypt 的 AES-CBC 加密后数据未保存到数据库或未正确解密【英文标题】:Data is not saved into database or not properly decrypted after adding MCrypt's AES-CBC encryption 【发布时间】:2015-08-06 02:09:00 【问题描述】:

我编写的加密脚本有问题。我有数百个输入,因此我使用桶排序算法作为“脚本内”数据库,以避免处理数百个 mysql 列的麻烦,更不用说它节省了代码空间。数据存储在 SQL 数据库的一列中。条目由' 分隔,该条目中的变量由> 分隔。这就是为什么我用字符串替换加密的最终结果......以避免产生额外的分隔符。

加密:

$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM);
$key = pack("H*", "**64CharacterStringHere**");

$value = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $value, MCRYPT_MODE_CBC, $iv);

$value = $iv . $value;

$value = str_replace (">", "9Y6SnNmOBl", $value);
$value = str_replace ("'", "SxsNEpBe18", $value);   

解密:

$value = str_replace ("9Y6SnNmOBl", ">", $value);
$value = str_replace ("SxsNEpBe18", "'", $value);

$iv = substr($value, 0, 16);
$value = substr($value, 16);

$key = pack("H*", "**64CharacterStringHere**");

$value = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $value, MCRYPT_MODE_CBC, $iv);

如果我在我的代码中省略了这个 sn-p,它就可以完美运行。数据正常显示和保存。但是,如果我添加此代码,有时数据根本不会被保存,并且如果它至少有一个变量由于某种原因没有被解密,即使我 100% 确定该函数正在每个变量上运行,因为该函数不仅仅是加密和解密。

我完全不知道为什么会这样。

MySQL 查询:

$new_accountant = "'" . $var_1 . ">" . $var_2;
$new_string = $string . $new_accountant;

$new_entry_query = 'UPDATE phone_tree SET string="' . $new_string  . '" WHERE id="' . $userid . '";';
mysql_query($new_entry_query);

在上面的脚本中,$string 由 MySQL 查询定义,该查询获取包含迄今为止所有数据的列的当前值。

我现在正在搞砸它。它根本没有保存大约一半的时间,当它被保存时,变量在大约一半的时间内没有被正确解密。

我怀疑这与我存储 IV 的方式有关……这与变量有关,但我不知道我在做什么是一个问题。

【问题讨论】:

这个代码看起来不错,除了可能错误的方式来划分不应该产生你描述的失败率的东西。我认为您还应该围绕加密部分提供一部分 mysql 代码。也许有一些线索。 我包含了我在编辑数据时运行的 MySQL 查询的 sn-p。 @ArtjomB。您所说的“可能是错误的分隔方式”是指由于加密字符串是二进制字符串替换 ASCII 字符将不起作用吗?有什么选择?我必须删除任何额外的分隔符。 php.net/str_replace, "注意:这个函数是二进制安全的。" @Allenph 采用以下可能的 IV+密文:as>9Y6SnNmOBlgfdalisduffliasufdn。然后您将替换导致as9Y6SnNmOBl9Y6SnNmOBlgfdalisduffliasufdn 的分隔符。在解密之前,您将替换分隔符并获得:as>>gfdalisduffliasufdn,这不是您开始使用的密文。 【参考方案1】:

在我看来,您的 MySQL string 列是使用 varchar(nnn) 数据类型定义的。对吗?

如果是这样,我建议您在将加密材料存储到 MySQL 之前对其进行 base-64 编码,然后再对其进行 base-64 解码?这是加密信息传输和存储的常见做法。它使您的材料字符串安全。 MySQL 可能会攻击您的二进制数据。

base-64 字符集(由 php 中实现的 base-64 的 MIME 版本使用)仅包含 7 位 ASCII 字符。它包括数字、大小写字母和字符+/=。这很好,因为您选择的分隔符 (> ') 不会出现在编码文本中,因此您可以摆脱符号填充 str_replace ("9Y6SnNmOBl", ">", $value) 代码。

在编码时执行此操作,并跳过符号填充。

$value = base64_encode($iv . $value);

在解码时执行此操作,并跳过符号 unstuffing。

$value = base64_decode($value);
$iv = substr($value, 0, 16);
$value = substr($value, 16);

这会使您的加密数据比其他情况大 1.3 倍。在现代大容量存储价格中,这并不算多,而且为简单而付出的合理价格。如果存在无法克服的成本障碍,您可以研究 MySQL 中的压缩行格式。

【讨论】:

美丽。它有效,但我仍然有点不确定发生了什么。如果我理解正确。该列确实是 VarChar。我的分隔符不会出现在字符串中,因为这些字符不是由 MCrypt 生成的?如果我正确理解另一部分,PHP 只使用 7 位字符,当我上传到数据库时,MySQL 会尝试从这些 7 位字符创建 8 位字符? varchar() 列具有隐含的字符集。我不知道您使用哪一个来存储您的数据。但是,允许 MySQL 更改不属于该字符集的文本字符串中的数据。您正在处理二进制数据。您也可能成功地将 varbinary() 数据类型与您之前的策略一起使用。不过,符号填充仍然是一个令人讨厌的 hack。 是的。现在我有一个问题,当数据显示在输入中以进行编辑时,它会被未知的二进制字符填充。

以上是关于添加 MCrypt 的 AES-CBC 加密后数据未保存到数据库或未正确解密的主要内容,如果未能解决你的问题,请参考以下文章

AES (aes-cbc-128, aes-cbc-192, aes-cbc-256) 使用 openssl C 加密/解密

尝试使用 BouncyCastle 的 AES-CBC 来解密加密文本的问题

golang实现aes-cbc-256加密解密过程记录

PHP mcrypt加密扩展使用总结

mcrypt 加密将 s 束 '%00' 添加到字符串末尾

使用 PHP mcrypt 加密后使用 Javascript CryptoJS 解密 AES