【问题标题】:Search Encrypted MySQL Database搜索加密的 MySQL 数据库
【发布时间】:2021-01-14 09:22:26
【问题描述】:

假设 MySQL 数据库有两个字段:名称和电子邮件。此外,假设出于隐私考虑,这两个字段都需要在静止时进行安全加密。并假设第三个字段包含一个 GUID,它是用于加密名称/电子邮件字段的密钥。 (第三个字段值与另一个值一起用作加密密钥,以减少所有加密都拥有“主”密钥的可能性。)

应用程序使用 PHP(7.4+ 版)和 MySQL。

当数据被静态加密时,您将如何在数据库中搜索明文值(姓名或电子邮件)?

已添加 数据将使用 openssl_encrypt() 加密。 (并将上面的内容更改为 MySQL。)

【问题讨论】:

  • 请解释为什么您认为将加密密钥存储在密文旁边是最安全的。
  • 仅供参考:没有“MySQLi 数据库”之类的东西。该软件名为 MySQL
  • 作为搜索本身 - 好吧,加密输入值然后搜索匹配项
  • 这能回答你的问题吗? Search filter on encrypted data in MySQL
  • @Sammitch - 哪一部分是个坏主意?也许将密钥存储为每行的一部分。所以全局键会更好。但是必须有一种很好的安全方式来存储全局密钥?或者可能是一个定期安排的工作,它会用新密钥重新加密所有内容?也许是 CipherSweet 使用的方法? paragonie.com/blog/2019/01/…

标签: php mysql encryption


【解决方案1】:

当数据被静态加密时,您将如何在数据库中搜索明文值(姓名或电子邮件)?

您不能一般对任意加密的数据执行此操作,但是像 CipherSweet 这样的库可以让这非常容易。

文档应该可以帮助您安装库。

要使用它,您首先需要setup a KeyProvider,然后创建一个EncryptedRow 对象,如下所示:

<?php
use ParagonIE\CipherSweet\BlindIndex;
use ParagonIE\CipherSweet\CipherSweet;
use ParagonIE\CipherSweet\CompoundIndex;
use ParagonIE\CipherSweet\EncryptedRow;
use ParagonIE\CipherSweet\Backend\FIPSCrypto;
use ParagonIE\CipherSweet\KeyProvider\StringProvider;

// Example key provider
$keyProvider = new StringProvider('4e1c44f87b4cdf21808762970b356891db180a9dd9850e7baf2a79ff3ab8a2fc');
// Setup the engine with a key provider. FIPSCrypto uses OpenSSL.
$engine = new CipherSweet($provider, new FIPSCrypto());

// EncryptedRow object setup:
$rowProcessor = (new EncryptedRow($engine, 'your_table_name_here'))
    ->addTextField('name')
    ->addTextField('email');
$rowProcessor->addBlindIndex(
    'name',
    new BlindIndex(
        'my_table__name_literal',
        16 /* See below */
    )
);
$rowProcessor->addBlindIndex(
    'email',
    new BlindIndex(
        'my_table__email_literal',
        16 /* See below */
    )
);

在此示例中,我添加了两个盲索引,每个索引的大小为 16 位。这是一个捏造的数字;您需要查看 blind index planning 以在此处使用哪些值作为指导。

现在您需要在将数据读/写到 MySQL 数据库时更新您的代码,使用 $rowProcessor 透明地即时加密/解密您的数据(以差异格式):

 /** @var array<string, mixed> $inputValues */
 - $db->insert('your_table_name_here', $inputValues);
 + [$inputValuesSomeEncrypted, $indices] = $rowProcessor->prepareForStorage($inputValues);
 + // If you wish to store the blind indexes in the same table:
 +     $inputValuesSomeEncrypted['name_idx'] = $indices['my_table__name_literal'];
 +     $inputValuesSomeEncrypted['email_idx'] = $indices['my_table__email_literal'];
 + $db->insert('your_table_name_here', $inputValuesSomeEncrypted);

接下来,您需要调整查找逻辑。

- $rows = $db->lookup("name = ? OR email = ?", [$row['name'], $row['email']]);
+ $index1 = $rowProcessor->getBlindIndex('my_table__name_literal', $row);
+ $index2 = $rowProcessor->getBlindIndex('my_table__email_literal', $row);
+ $rowsWithCiphertext = $db->lookup("name_idx = ? OR email_idx = ?", [$index1, $index2]);
+
+ /* We need to post-process to eliminate coincidences in the blind index */
+ $rows = [];
+ foreach ($rowsWithCiphertext as $rowC) {
+     $decrypted = $rowProcessor->decryptRow($rowC);
+     if (!hash_equals($decrypted['name'], $row['name']) && !hash_equals($decrypted['email'], $row['email'])) {
+         continue;
+     }
+     $rows[] = $decrypted;
+ }

根据您的原始代码的外观,必要的确切代码更改看起来会有所不同。我只是想在这里演示一下结构。

这将允许您将加密数据存储在数据库中仍然在 SQL 查询的 WHERE 子句中使用提供的 nameemail 参数(带有抽象层)。

【讨论】:

    猜你喜欢
    • 2012-12-18
    • 2016-02-28
    • 2021-06-29
    • 2018-03-21
    • 2014-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多