使用PHP安全地加密MySQL数据库中静止的客户详细信息

Ben*_* CA 2 php encryption

我正在使用PHP开发一个应用程序,该程序将一些客户详细信息存储在MySQL数据库中。(姓名,电子邮件地址,电话号码,邮寄地址)我没有存储任何“敏感”信息,例如银行/信用卡详细信息,SSN / SIN,DOB等。仅是基本的客户详细信息。

但是,尽管显然会采取安全预防措施,但如果黑客获得了数据库的副本,我希望有一种体面且相对简单的方法来使此数据非常难以使用(通过对其进行加密)。

我已经读到,在MySQL查询中执行加密的安全性较低,因为加密密钥将缓存在数据库的查询日志中。

因此,建议这样:

UPDATE customers SET email = AES_ENCRYPT('me@gmail.com', SHA2('encryption key here', 512));
Run Code Online (Sandbox Code Playgroud)

我已经看过关于堆栈溢出的许多问题以及其他资源。但是,许多建议至少有5年的历史了,现在PHP 7.2(及更高版本)可能会有更简单的最佳实践。

我研究过Defuse,但是我倾向于避免在我不是绝对需要它们的地方使用第三方库。(我更喜欢理解代码并将其最小化,以满足我的需求。)

在查看PHP文档(https://www.php.net/manual/zh/function.openssl-encrypt.php)时,我发现该用户提供了建议,该建议看起来很容易实现:

--- Create Two Random Keys And Save Them In Your Configuration File ---
<?php
    // Create The First Key
    echo base64_encode(openssl_random_pseudo_bytes(32));

    // Create The Second Key
    echo base64_encode(openssl_random_pseudo_bytes(64));
?>
--------------------------------------------------------
<?php
    // Save The Keys In Your Configuration File
    define('FIRSTKEY', 'Lk5Uz3slx3BrAghS1aaW5AYgWZRV0tIX5eI0yPchFz4=');
    define('SECONDKEY', 'EZ44mFi3TlAey1b2w4Y7lVDuqO+SRxGXsa7nctnr/JmMrA2vN6EJhrvdVZbxaQs5jpSe34X3ejFK/o9+Y5c83w==');
?>
--------------------------------------------------------
<?php
    function secured_encrypt($data)
    {
        $first_key = base64_decode(FIRSTKEY);
        $second_key = base64_decode(SECONDKEY);

        $method = "aes-256-cbc";
        $iv_length = openssl_cipher_iv_length($method);
        $iv = openssl_random_pseudo_bytes($iv_length);

        $first_encrypted = openssl_encrypt($data, $method, $first_key, OPENSSL_RAW_DATA, $iv);
        $second_encrypted = hash_hmac('sha3-512', $first_encrypted, $second_key, TRUE);

        $output = base64_encode($iv.$second_encrypted.$first_encrypted);
        return $output;
    }
?>
--------------------------------------------------------
<?php
    function secured_decrypt($input)
    {
        $first_key = base64_decode(FIRSTKEY);
        $second_key = base64_decode(SECONDKEY);
        $mix = base64_decode($input);

        $method = "aes-256-cbc";
        $iv_length = openssl_cipher_iv_length($method);

        $iv = substr($mix, 0, $iv_length);
        $second_encrypted = substr($mix, $iv_length, 64);
        $first_encrypted = substr($mix, $iv_length+64);

        $data = openssl_decrypt($first_encrypted, $method, $first_key, OPENSSL_RAW_DATA, $iv);
        $second_encrypted_new = hash_hmac('sha3-512', $first_encrypted, $second_key, TRUE);

        if (hash_equals($second_encrypted, $second_encrypted_new))
            return $data;

        return false;
    }
?>
Run Code Online (Sandbox Code Playgroud)

您认为这相当安全吗?我以为我可能会改用AES-256-GCM,因为我认为GCM比CBC更好。

这样的加密是否满足我的需求?(同样,不会存储任何高度敏感的客户详细信息,并且“希望”这种安全级别(静态加密数据库字段)仍然是多余的。)

如果我跳过hash_hmac sha3-512部分而只使用openssl_encrypt函数怎么办?

Sco*_*ski 11

我研究过Defuse,但是我倾向于避免在我不是绝对需要它们的地方使用第三方库。(我更喜欢理解代码并将其最小化,以满足我的需求。)

由于您的问题标题是使用PHP安全地加密MySQL数据库中静态的客户详细信息,因此我将不得不将答案分为两部分。

在保护客户数据的背景下,您(以及其他任何人)将收到的建议是使用可信赖的库

即使对于专家来说,密码学也很难做到正确。就在这个月,对许多低级密码库的攻击。但是,多年来我在PHP社区中推荐的库(即libsodium )对这些攻击仍然没有影响(大部分是设计使然)。

我和其他专家推荐的库旨在最大程度地提高安全性,最大程度地减少误用的可能性,并且易于审核。避免使用这些建议是因为您不想使用第三方库,尤其对于加密而言,这是一个危险的立场。

如果您对“避免使用第三方库”的渴望恰好比保护客户的要紧要紧,那么您可能应该告诉客户您在做什么,为什么做以及安全行业的传统智慧;因此他们可以决定是否仍然要成为您的客户。

反过来说,如果您说出某种效果:“这是出于我自己的自我教育,没有现实世界的生产系统”,那么那是完全独立的事情。毕竟,编写加密货币来学习是一件好事

在PHP + MySQL中安全加密静态的客户详细信息

建议:使用CipherSweet

composer require paragonie/ciphersweet:^2
Run Code Online (Sandbox Code Playgroud)

请记住,答案的这一部分是“我需要保护客户数据”。您不希望安装第三方库的愿望需要解决。保护客户是当务之急。

CipherSweet去几个步骤进一步比化解:不是仅仅解决“对称的静止数据加密”的问题,也CipherSweet解决了“我要对数据进行加密,但仍然搜索就可以了”的问题。

CipherSweet是可插入的;我们提供了两个后端(FIPSCryptoModernCrypto),但提供了一个接口,如果有人需要编写自己的接口,可以使用该接口。(例如,如果出于某种原因需要SHA3和AES-GCM,则可以编写自己的FIPSCrypto变体。)

CipherSweet文档可在线获得

它的目的不仅是解释幕后情况,还指导开发人员安全地使用它。如果您遇到任何麻烦,请随时寻求帮助。

如果您确定不需要任何“可搜索”位,则仍可以使用CipherSweet,而无需为简单的AEAD接口创建盲索引。

另外,Defuse和Halite是不错的选择。

但是重要的是,除非您是能够重新创建这些第三方库中的一个而不会意外引入漏洞的密码学工程师(例如:定时攻击或AES-CBC + HMAC,但忘记在身份验证中包含IV标签计算),那么您几乎可以肯定应该使用第三方库

没有客户参与,我只想学习

我发现该用户提供了建议,该建议看起来相当简单容易实现:

您提供的代码是不是安全。具体来说,这部分代码没有涵盖HMAC标签计算中的IV:

$first_encrypted = openssl_encrypt($data, $method, $first_key, OPENSSL_RAW_DATA, $iv);
$second_encrypted = hash_hmac('sha3-512', $first_encrypted, $second_key, TRUE);
Run Code Online (Sandbox Code Playgroud)

请注意如何$first_encrypted传递给,hash_hmac()$iv不是?这是一个可利用的漏洞。

这是概念证明的利用:

// Encrypt a message    
$ciphertext = secured_encrypt('{"is_admin":0,"user_id":12345}');

$decoded = base64_decode($ciphertext);
$extractedIv = mb_substr($decoded, 0, 16, '8bit');
$flip = "\x00\x00\x00\x00\x00\x00\x00\x00" . 
        "\x00\x00\x00\x00\x01\x00\x00\x00";
$extractedIv = $extractedIv ^ $flip;

// Put alternative IV in place of existing IV.
$spliced = $extractedIv . mb_substr($decoded, 16, null, '8bit');
$reEncoded = base64_encode($spliced);

// Decrypt message
$decrypted = secured_decrypt($reEncoded);

var_dump($decrypted, json_decode($decrypted, true));
Run Code Online (Sandbox Code Playgroud)

您应该看到以下内容:

string(30) "{"is_admin":1,"user_id":12345}"
array(2) {
  ["is_admin"]=>
  int(1)
  ["user_id"]=>
  int(12345)
}
Run Code Online (Sandbox Code Playgroud)

因此,CBC + HMAC对开发人员来说很危险。

你好得多使用libsodium并学习如何编写自己的高层协议将其提供比试图让CBC + HMAC是安全的原语之上。

如果要修复他们的代码,则需要进行这些更改。

补丁的secure_encrypt()

- $second_encrypted = hash_hmac('sha3-512', $first_encrypted, $second_key, TRUE);
+ $second_encrypted = hash_hmac('sha3-512', $iv . $first_encrypted, $second_key, TRUE);
Run Code Online (Sandbox Code Playgroud)

补丁的secure_decrypt()

- $second_encrypted_new = hash_hmac('sha3-512', $first_encrypted, $second_key, TRUE);
+ $second_encrypted_new = hash_hmac('sha3-512', $iv . $first_encrypted, $second_key, TRUE);
Run Code Online (Sandbox Code Playgroud)

您问题的答案

我以为我可能会改用AES-256-GCM,因为我认为GCM比CBC更好。

AES-GCM比CBC更好,因为GCM已通过身份验证,而CBC未通过身份验证

但是,在AES-GCM的给定密钥下,您只能加密大约2 ^ 36条消息。如果这不足以实现您的目标,那么AES-GCM也不适合。

这样的加密是否满足我的需求?(同样,不会存储任何高度敏感的客户详细信息,并且“希望”这种安全级别(静态加密数据库字段)仍然是多余的。)

如果不是因为我上面披露的漏洞,那就可能是。

如果我跳过hash_hmac sha3-512部分而只使用openssl_encrypt函数怎么办?

如果您切换到AES-GCM或libsodium,则只能放心使用它。未经身份验证的加密永远不应该被信任。

  • 斯科特,我很欣赏详细而内容丰富的答复。我也在 Pargonie.com 上看过您的一些博客文章。您是一位专家,我希望我知道的安全知识只有您的 10%。我并不完全反对第三方库,但我不喜欢安装和修补这么多不同的库及其依赖项。我希望 PHP 默认内置一些库(如 openssl_encrypt);那么更新 PHP 就可以实现这一点。 (3认同)
  • 当我看到这样的答案时,我希望 stackoverflow 能获得像 Reddit 这样的奖项!多谢! (2认同)