关于php:Laravel AES-256加密和MySQL

Laravel AES-256 Encryption & MySQL

我正在尝试通过使用AES-256-CBC的Laravel(5.3)加密方法来加密敏感的用户信息。 加密数据后,我想将其存储在MySQL数据库中,但是我不知道应将其另存为哪种类型,也不知道其长度。

任何帮助表示赞赏。


更新资料

PR 31721已合并到Laravel 7.0.8中,该版本修复了json编码中转义的正斜杠。在此之前,加密相同的数据将为您提供可变大小的结果。现在,从7.0.8版开始,加密相同的数据将为您每次提供相同的大小结果。

TL; DR:

Laravel的crypto方法将返回一个字符串,因此数据类型应为varchar或文本变体,具体取决于要加密的数据的大小。

要确定近似大小,可以使用以下一系列计算:

Laravel> = 7.0.8

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个字符。

Laravel <7.0.8

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个字符,这是超出预期范围的正常缓冲区,但从统计上讲不可能命中。

细节:

从crypto方法返回的值不仅是加密的文本,而且是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中,base64编码数据中的任何正斜杠都会在json字符串中带有反斜杠进行转义,因此这会根据存在的正斜杠的数量添加可变数量的字节。对于SSN示例,base68编码数据有68个字符(加密数据为44个字符,IV为24个字符)。让我们假设正斜杠的最大数量可能约为结果的1/3,或大约23个额外的字节。在Laravel> = 7.0.8中,这些正斜杠不会转义,因此没有多余的字节。

最后,这个json_encoded值是base64_encoded,它将再次将大小增加约4/3。

因此,将所有这些放在一起,再假设您正在加密一个社会保险号。 openssl_encrypt结果将为44个字符,MAC为64个字符,IV为24个字符,并且json表示形式再添加29个字符。

在Laravel <7.0.8中,还有额外的23个字符的缓冲区。 这给我们(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迁移中,它将是

1
$table->binary('data');

要么

1
$table->longText('description');