【问题标题】:Laravel AES-256 Encryption & MySQLLaravel AES-256 加密和 MySQL
【发布时间】:2017-06-14 20:38:07
【问题描述】:

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

感谢任何帮助。

【问题讨论】:

    标签: php mysql laravel encryption aes


    【解决方案1】:

    更新

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

    TL;DR:

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

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

    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

    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 编码的有效负载数组的 ba​​se64 编码表示,其中包含 (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) 字符。

    【讨论】:

      【解决方案2】:

      您将其保存为(加密的)文本,所以... 你应该使用 longtext/blob 作为 mysql 中的字段类型

      在 laravel 迁移中会是

      $table->binary('data');  
      

      $table->longText('description');
      

      【讨论】:

      • AES 是否对每个输出使用相同的长度,即 encrypt('password') 和 encrypt('passwordpassword') 产生相同的文本长度,如果是,那是多长时间?谢谢。
      • 正如文档所说,加密返回序列化数据,并且有解密选项 - 所以我认为不同输入的长度会不同。相同的长度只会在单向加密方法(MD5/SHA)中
      • 大小显然取决于输入的大小。通常,与输入 以字节为单位 的大小相比,最多将添加 16 个额外字节。所以必须有某种(隐式)从字符到字节的转换(例如 UTF-8)。可变二进制在这里很有意义。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-20
      • 2018-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多