【发布时间】:2017-06-14 20:38:07
【问题描述】:
我正在尝试通过 Laravel 的 (5.3) 加密方法加密敏感的用户信息,该方法使用 AES-256-CBC。加密数据后,我想将其存储在我的 MySQL 数据库中,但我不知道应该将其保存为哪种类型,也不知道它的长度。
感谢任何帮助。
【问题讨论】:
标签: php mysql laravel encryption aes
我正在尝试通过 Laravel 的 (5.3) 加密方法加密敏感的用户信息,该方法使用 AES-256-CBC。加密数据后,我想将其存储在我的 MySQL 数据库中,但我不知道应该将其保存为哪种类型,也不知道它的长度。
感谢任何帮助。
【问题讨论】:
标签: php mysql laravel encryption aes
PR 31721 已合并到 Laravel 7.0.8 中,它修复了 json 编码中转义的正斜杠。在此之前,加密相同的数据会给你带来可变大小的结果。现在,从 7.0.8 开始,加密相同的数据每次都会得到相同大小的结果。
Laravel 的 encrypt 方法将返回一个字符串,因此数据类型应该是 varchar 或 text 变体,具体取决于被加密数据的大小。
要确定大致大小,可以使用以下一系列计算:
设a = 序列化未加密数据的大小(strlen(serialize($data)))
让b = a + 16 - (a MOD 16)(计算加密数据的大小)
让c = (b + 2 - ((b + 2) MOD 3)) / 3 * 4(计算base64编码数据的大小)
设d = c + 117(加上MAC、IV、json编码的大小)
让e = (d + 2 - ((d + 2) MOD 3)) / 3 * 4(计算base64编码数据的大小)
尽管值不是确定性的,但结果的大小是确定性的。例如,如果您要加密一个 9 位数的社会安全号码,结果将始终是 216 个字符。
设a = 序列化未加密数据的大小(strlen(serialize($data)))
让b = a + 16 - (a MOD 16)(计算加密数据的大小)
让c = (b + 2 - ((b + 2) MOD 3)) / 3 * 4(计算base64编码数据的大小)
让d = c + 117 + 8 + ((c + 2 - ((c + 2) MOD 3)) / 3)(添加 MAC、IV 和 json 编码的大小,以及用于可能转义的斜杠的额外缓冲区)
让e = (d + 2 - ((d + 2) MOD 3)) / 3 * 4(计算base64编码数据的大小)
例如,如果您要加密一个 9 位数的社会安全号码,则结果最少为 216 个字符,最多为 308 个字符(尽管这可能在统计上是不可能的)。如果您运行 100000+ 个加密循环,您会看到大小通常在 216 - 224 范围内。上面提供的公式会告诉您将字段设置为 248 个字符,这是一个超出预期范围的健康缓冲区,但在统计上并非不可能达到。
从 encrypt 方法返回的值不仅仅是加密文本,而是一个 json 编码的有效负载数组的 base64 编码表示,其中包含 (1) 序列化数据的 base64 编码加密值,(2) base64 编码初始化向量 (IV),以及 (3) 消息认证码 (MAC)。因此,要确定所需字段的大小,您需要知道将被编码的数据的最大大小,然后为填充在返回字符串中的这些额外信息添加一些额外的空间。
首先,让我们计算加密值的最大大小。由于您的加密算法 (AES-256-CBC) 是分组密码,因此使用公式很容易完成。 AES 使用 16 字节块并且需要至少一个字节的填充,因此加密值的大小将是 16 的下一个倍数。因此,如果您的原始数据是 30 字节,那么您的加密数据将是 32 字节。如果您的原始数据是 32 字节,那么您的加密数据将是 48 字节(因为 AES 需要至少一个字节的填充,所以您的 32 字节变为 33,然后上升到下一个 16 到 48 的倍数)。这个公式是x + 16 - (x MOD 16)。因此,对于 30 个字节,您将获得 30 + 16 - (30 MOD 16) = 32。
在计算加密值的大小时,请记住被加密的数据首先被序列化。因此,例如,如果您正在加密一个社会安全号码,则纯值只有 9 个字符,但序列化值实际上是 16 个字符(s:9:"xxxxxxxxx";)。由于序列化后的值是实际加密的,是 16 字节,所以加密后的值大小为 32 字节(16 + 16 - (16 MOD 16) = 32)。
除此之外,openssl_encrypt 函数还返回已经经过 base64 编码的加密数据。 Base64 编码将值的大小增加了大约 4/3。对于原始数据中的每 3 个字节,base64 编码将生成一个 4 字节(字符)表示。因此,对于 SSN 示例,加密结果为 32 个字节。当转换为 base64 时,32 字节给我们(32 / 3) = 10.6 3 字节段。由于 base64 填充到下一个字节,取上限,然后乘以 4,得到11 * 4 = 44 字节。所以,我们原来的 32 字节加密值变成了 44 个字符的字符串。如果你需要一个公式,你可以使用(x + 2 - ((x + 2) MOD 3)) / 3 * 4。所以,(32 + 2 - ((32 + 2) MOD 3)) / 3 * 4 = 44。
下一条信息是 MAC。 MAC 是一个 SHA256 哈希值,因此我们知道它将是 64 个字符。
最后一条信息是 IV。普通 IV 是 16 个随机字节。存储在有效负载数组中的 IV 是普通 IV 的 base64 编码值。所以,我们可以使用上面的公式来计算base64编码的IV的大小:(16 + 2 - ((16 + 2) MOD 3)) / 3 * 4 = 24。
这三条信息被压缩成一个数组,然后json_encoded。由于 json 表示和数组中值的名称,这又增加了 29 个字节。
此外,在 Laravel = 7.0.8 中,这些正斜杠没有被转义,所以没有多余的字节。
最后,这个 json_encoded 值是 base64_encoded,这将再次增加大约 4/3 的大小。
因此,将所有这些放在一起,让我们再次假设您正在加密一个社会安全号码。 openssl_encrypt 结果将是 44 个字符,MAC 是 64 个字符,IV 是 24 个字符,而 json 表示又增加了 29 个字符。
在 Laravel 44 + 64 + 24 + 29 + 23 = 184) 个字符。这个结果被 base64 编码,这给了我们 ((184 + 2 - ((184 + 2) MOD 3)) / 3 * 4 = 248) 个字符。
在 Laravel >= 7.0.8 中,没有额外的缓冲区。这给了我们 (44 + 64 + 24 + 29 = 161) 个字符。这个结果得到了 base64 编码,这给了我们 ((161 + 2 - ((161 + 2) MOD 3)) / 3 * 4 = 216) 字符。
【讨论】:
您将其保存为(加密的)文本,所以... 你应该使用 longtext/blob 作为 mysql 中的字段类型
在 laravel 迁移中会是
$table->binary('data');
或
$table->longText('description');
【讨论】: