Laravel AES-256 加密和 MySQL

ea.*_*d12 2 php mysql encryption aes laravel

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

任何帮助表示赞赏。

pat*_*cus 5

更新

PR 31721已合并到 Laravel 7.0.8 中,修复了 json 编码中转义的正斜杠。在此之前,加密相同的数据会得到不同大小的结果。现在,从 7.0.8 开始,加密相同的数据每次都会得到相同大小的结果。

长话短说:

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

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

Laravel >= 7.0.8

Let a= 序列化未加密数据的大小 ( strlen(serialize($data)))
Let b= a + 16 - (a MOD 16)(计算加密数据的大小)
Let c= (b + 2 - ((b + 2) MOD 3)) / 3 * 4(计算base64编码数据的大小)
Let d= c + 117(添加MAC、IV和json编码的大小)
Let e= (d + 2 - ((d + 2) MOD 3)) / 3 * 4(计算Base64 编码数据)

尽管该值不是确定性的,但结果的大小是确定性的。例如,如果您要加密 9 位社会安全号码,结果将始终是 216 个字符。

拉维尔 < 7.0.8

Let a= 序列化未加密数据的大小 ( strlen(serialize($data)))
Let b= a + 16 - (a MOD 16)(计算加密数据的大小)
Let c= (b + 2 - ((b + 2) MOD 3)) / 3 * 4(计算base64编码数据的大小)
Let d= c + 117 + 8 + ((c + 2 - ((c + 2) MOD 3)) / 3)(添加MAC、IV和json编码的大小,加上可能转义的额外缓冲区斜杠)
Let e= (d + 2 - ((d + 2) MOD 3)) / 3 * 4(计算base64编码数据的大小)

例如,如果您要加密 9 位社会安全号码,则结果将至少为 216 个字符,最多为 308 个字符(尽管这在统计上可能是不可能的)。如果您运行超过 100000 个加密的循环,您会看到大小通常在 216 - 224 范围内。上面提供的公式会告诉您将字段设置为 248 个字符,这是高于预期范围的健康缓冲区,但从统计角度来看并非不可能达到。

细节:

加密方法返回的值不仅仅是加密文本,而且是 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.63 字节段。由于 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 示例,base64 编码数据有 68 个字符(44 个字符用于加密数据,24 个字符用于 IV)。我们假设正斜杠的最大数量可能约为结果的 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) 字符。