如何在 PHP 中没有硬编码密钥的情况下进行对称加密

Posted

技术标签:

【中文标题】如何在 PHP 中没有硬编码密钥的情况下进行对称加密【英文标题】:How to do symmetric cryptography without hardcoded key in PHP 【发布时间】:2016-11-14 03:46:49 【问题描述】:

我正在使用 php mcrypt 库使用 AES 加密和存储 (mysql) 数据。

我想知道是否有一种好方法可以做到这一点,而无需在我的代码中使用硬编码的加密/解密密钥。

如果黑客访问了我的服务器,他将能够看到文件和我在代码中的密钥,从而访问数据库上的所有数据。

谢谢。

【问题讨论】:

你需要一个钥匙。最好将其存储在您的代码中,而不是存储在加密数据所在的数据库中。 是的,但我想知道是否有更安全的方法。将密码存储在代码中会破坏整点。唯一有效的情况是黑客只能访问数据库,而不是整个服务器。 没错。这通常是重点。如果他们获得了数据库转储或对数据库服务器的访问权,他们将无法使用该信息。如果他们能够访问网络服务器,那并不重要,因为他们将能够做任何事情,包括拦截呼叫/数据和逆向工程 一般来说,如果服务器被入侵,那么一切都会被入侵,所以在服务器上对称地加密你的数据库数据并没有真正的帮助,所以不要这样做。 使用 AES 加密数据是存储信用卡信息的常规做法。您无法绕过将密钥存储在网络服务器上。 【参考方案1】:

我正在使用 PHP mcrypt 库使用 AES 加密和存储 (MySQL) 数据。

You may wish to reconsider your choice in cryptography library.

我想知道是否有一种好方法可以做到这一点,而无需在我的代码中使用硬编码的加密/解密密钥。

将其存储在文档根目录之外的配置文件中?例如,defuse/php-encryption。

如果黑客访问了我的服务器,他将能够看到文件和我在代码中的密钥,从而访问数据库上的所有数据。

如果黑客可以访问您的服务器,symmetric-key encryption cannot save you。但是,公钥加密可以保护机密性。

使用Halite,这很容易解决:

    只能在服务器上加密;从不解密。 您的密钥必须保持离线状态并供人使用。

在线代码(假设 PHP 7.0 和 Halite 2.1)

<?php
declare(strict_types=1);
use ParagonIE\Halite\
    Asymmetric\Crypto as Asymmetric,
    KeyFactory
;

$publicKey = KeyFactory::loadEncryptionPublicKey("/path/to/public/key");
$encrypted = Asymmetric::seal("Whatever secret data we want", $publicKey);
// Now do whatever you need with $encrypted

离线代码(假设 PHP 7.0 和 Halite 2.1)

<?php
declare(strict_types=1);
use ParagonIE\Halite\
    Asymmetric\Crypto as Asymmetric,
    KeyFactory
;

$salt = ""; // Generate from random_bytes(16) once, then persist.
$password = ""; // Create a strong password

$keyPair = KeyFactory::deriveEncryptionKeyPair($password, $salt);
$secretKey = $keyPair->getSecretKey();
$publicKey = $keyPair->getPublicKey();

// To have the public key to a file to upload to the server:
   KeyFactory::save($publicKey, '/path/to/public/key');

$decrypted = Asymmetric::unseal($encrypted, $secretKey);

【讨论】:

谢谢,这是一个很好的答案!我决定使用公钥来加密数据。这样,如果我被黑客入侵,黑客将无法访问私钥。太棒了! 我正在使用 PHP 的 openssl_public_encrypt。这是个好主意吗? Only if you use it very carefully. 也许这是一个单独的问题,但是一旦设置了密码,有没有办法更改密码?否则我能看到的唯一方法是解密所有内容然后重新加密。 示例代码从密码派生密钥。 Protecting a key with a password without deriving the key from the password 可以通过 Defuse 的 PHP 加密库实现。【参考方案2】:

这取决于你愿意走多远,以及你的环境。

将解密密钥保存在数据库中绝对是个坏主意 - 如果有人掌握了数据库,他们将同时拥有解密密钥和数据。通过将其存储在应用程序服务器上,您可以确定不会发生上述情况。但是如果有人可以访问应用程序服务器,然后通过应用程序服务器访问数据库呢?现在他们又拥有了密钥和数据。不过你已经说了这么多。

由于您没有提及您的环境,我们假设:

标准 LAMP 堆栈 PHP 作为 Apache 模块运行 您有一个部署工具/脚本来部署您的应用程序

你可以有一个简单的 Apache 配置文件:

将环境变量设置为加密密钥的值 从主要 apache 配置中包含 以加密方式存储在磁盘/存储库/部署工具可以访问的任何位置

然后在部署期间:

您的部署工具在其部署步骤中会尝试解密加密的配置文件并要求部署用户提供密钥 部署用户从完全未连接到正在运行的生产应用程序的系统/方法中提供密钥(例如离线安全密码存储等) 部署工具将文件复制到生产系统并解密 部署工具(重新)在生产系统上启动 Apache Apache加载解密后的配置文件,设置环境变量 一旦 Apache 运行,部署工具就会删除/覆盖/碎片/等。包含安全加密密钥的解密配置文件

在这之后,事情的当前状态将是:

Apache 已将配置加载到内存中,解密后的密钥可通过环境变量供 PHP 使用 安全密钥不存储在生产系统的任何位置,它仅在内存中可用 您可以重新加载/重新启动 Apache 的唯一方法是通过您的部署工具(这可能是您想要的)

你如何仍然容易受到攻击:

当您直接写入支持的文件系统上的磁性硬盘驱动器时,粉碎文件是安全的;它在 VM 或 SSD 环境中可能没有那么安全 有权访问应用程序服务器的攻击者可以转储 Apache 使用的内存并试图找出如何获取那里某处的解密密钥 在部署过程中的几秒钟内,当 Apache 加载时,文件在服务器上是未加密的;如果攻击者具有不间断的访问权限并且知道要查找的内容,他们可能会很幸运并找到该文件

不过,它比将未加密的密钥存储在应用程序服务器上要安全得多,而且它需要非常复杂且高度复杂的攻击者才能利用。所以,正如我在开头所说的,这取决于你想走多远。

【讨论】:

以上是关于如何在 PHP 中没有硬编码密钥的情况下进行对称加密的主要内容,如果未能解决你的问题,请参考以下文章

在给定明文和密文的情况下计算对称加密的关键

Android安全开发之浅谈密钥硬编码

如何在没有硬编码文件名的情况下以 Angular 4 下载文件?

什么是对称加密?

PHP 加密:AES & RSA

如何在没有硬编码的情况下使用多个不和谐机器人帐户登录?