如何使用 AES_ENCRYPT 和 PDO 准备语句改进大型加密数据库的 PHP 解决方法?

Posted

技术标签:

【中文标题】如何使用 AES_ENCRYPT 和 PDO 准备语句改进大型加密数据库的 PHP 解决方法?【英文标题】:How to improve the workaround in PHP for large encrypted databases using AES_ENCRYPT with PDO prepared statements? 【发布时间】:2021-11-18 07:06:55 【问题描述】:

mysql 中使用准备好的语句,您必须只使用一次参数。 像下面的例子这样的编码会调用“SQLSTATE[HY093]: Invalid parameter number”

$enigma = 'ThisIsTheSecretEncryptionKey';
$data = [
    'name' => $name,
    'first_name' => $first_name,
    'gender' => $gender,
    'birthdate' => $birthdate,
    'email' => $email,
    'profession' => $profession,
    'enigma' => $enigma
];

$sql = "INSERT INTO members
(name , firstname , gender , birthdate, email, profession)
VALUES(
    AES_ENCRYPT(:name, :enigma),
    AES_ENCRYPT(:first_name, :enigma),
    AES_ENCRYPT(:gender, :enigma),
    AES_ENCRYPT(:birthdate, :enigma)
    AES_ENCRYPT(:email, :enigma)
    AES_ENCRYPT(:profession, :enigma)
)";

$pdo->prepare($sql)->execute($data);

为了克服这个问题,我找到了这个解决方案:

$enigma = 'ThisIsTheSecretEncryptionKey';
$data = [
    'name' => $name,
    'first_name' => $first_name,
    'gender' => $gender,
    'birthdate' => $birthdate,
    'email' => $email,
    'profession' => $profession,
    'enigma' => $enigma,
    'enigma2' => $enigma,
    'enigma3' => $enigma,
    'enigma4' => $enigma,
    'enigma5' => $enigma,
    'enigma6' => $enigma,
];

$sql = "INSERT INTO members
(name , firstname , gender , birthdate, email, profession)
VALUES(
    AES_ENCRYPT(:name, :enigma),
    AES_ENCRYPT(:first_name, :enigma2),
    AES_ENCRYPT(:gender, :enigma3),
    AES_ENCRYPT(:birthdate, :enigma4)
    AES_ENCRYPT(:email, :enigma5)
    AES_ENCRYPT(:profession, :enigma6)
)";

$pdo->prepare($sql)->execute($data);

它可以工作,但它不是一个真正顺利的解决方案,尤其是当涉及到包含大量列的表时。 有没有其他方法在 MySQL 的加密数据库中使用准备好的语句?

【问题讨论】:

在您的两个示例中,您只定义了 4 列,但随后尝试设置 6 个值。通过示例,它们都不应该工作。 @MagnusEriksson - 感谢您的评论。我刚刚编辑了它。 【参考方案1】:

至少有两种选择

首先,您可以启用emulation mode for PDO(或者,不要在连接选项中禁用它)。在这种情况下,PDO 将开始对命名占位符做出明智的行为,并允许您重用它们,因此您只需定义一次。

$data = [
    'name' => $name,
    'first_name' => $first_name,
    'gender' => $gender,
    'birthdate' => $birthdate,
    'email' => $email,
    'profession' => $profession,
    'enigma' => $enigma,
];
$sql = "INSERT INTO members
(name , firstname , gender , birthdate, email, profession)
VALUES(
    AES_ENCRYPT(:name, :enigma),
    AES_ENCRYPT(:first_name, :enigma),
    AES_ENCRYPT(:gender, :enigma),
    AES_ENCRYPT(:birthdate, :enigma)
    AES_ENCRYPT(:email, :enigma)
    AES_ENCRYPT(:profession, :enigma)
)";

另一种选择是使用 SQL 变量。您可以运行一个查询(如果所有表都使用相同的谜,您可以在连接后立即在每个脚本执行时运行一次此查询)

$pdo->prepare("SET @aes_enigma=:enigma")->execute([$enigma]);

然后在你的查询中使用这个变量

$data = [
    'name' => $name,
    'first_name' => $first_name,
    'gender' => $gender,
    'birthdate' => $birthdate,
    'email' => $email,
    'profession' => $profession,
];
$sql = "INSERT INTO members
(name , firstname , gender , birthdate, email, profession)
VALUES(
    AES_ENCRYPT(:name, @aes_enigma),
    AES_ENCRYPT(:first_name, @aes_enigma),
    AES_ENCRYPT(:gender, @aes_enigma),
    AES_ENCRYPT(:birthdate, @aes_enigma)
    AES_ENCRYPT(:email, @aes_enigma)
    AES_ENCRYPT(:profession, @aes_enigma)
)";

但老实说,我会不惜一切代价避免使用加密数据库。可能是几个表中的一些选定字段。但是鉴于您不能对加密数据使用索引,因此只有一个最多几千行的玩具数据库才能真正使用。

【讨论】:

非常感谢! SQL 变量工作正常!

以上是关于如何使用 AES_ENCRYPT 和 PDO 准备语句改进大型加密数据库的 PHP 解决方法?的主要内容,如果未能解决你的问题,请参考以下文章

使用 PDO 准备参数化查询

如何使用带有 BIT(1) 列的 PDO 准备语句?

在 PHP 中到处使用准备好的语句? (PDO)

如何使用准备好的语句将表单数据插入 PDO?

如何使用准备好的 PDO 语句设置 ORDER BY 参数?

如何使用准备好的 PDO 语句设置 ORDER BY 参数?