这种数据加密/存储方法安全吗?

信息安全 加密 php
2021-08-19 07:42:30

首先让我说,我知道将敏感信息存储在 mysql 数据库中是一个坏主意,所以请不要回复说“不要这样做”或类似的话。我正在构建一个存储社会安全号码绝对必要的网站,并且我必须能够从数据库中检索该数据(无哈希)。

也就是说,我已经研究了加密/解密数据的最佳方法,并构建了一个自定义函数来处理加密。这是我的加密功能:

function my_data_encrypt($value){
    $salt=substr(uniqid('', true), 0, 20);
    $key=$salt.MY_PRIVATE_KEY;
    $enc_value=base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $value, MCRYPT_MODE_CBC, md5(md5($key))));
    return array("enc_value"=>$enc_value, "salt"=>$salt);
}

MY_PRIVATE_KEY所以基本上我正在为我的盐生成一个随机字符串,然后在盐上附加一个在单独的配置文件中定义的私钥。然后我用它mcrypt_encrypt来加密数据,然后base64_encode使加密安全地存储在数据库中。然后将加密的字符串和唯一的盐值一起返回并存储在数据库中。

我的想法是,将存储在配置文件(而不是数据库)中的“私钥”放入混合中会为加密增加另一个级别的安全性,即使有人破解了数据库并获得了加密值和盐,他们仍然没有解密数据所需的一切。

安全专家能否审查我的功能,并让我知道我的数据是否/如何被黑客入侵,以及我是否可以做任何其他事情来改进它?

2个回答

您正在使用一个 128 位密钥(MD5 的输出,因此是 128 位)和一个需要 256 位密钥 (Rijndael-256) 的分组密码。如果 PHP 实施得当,那么它会在你面前大放异彩。但是,文档指出密钥只是用零填充到所需的长度。如果您使用 128 位密钥,则最好使用期望 128 位密钥的密码,即 Rijndael-128。

“盐”要么没用,要么应用不当。Salts旨在处理弱密钥,可以通过穷举搜索实际恢复,即当密钥是密码时。如果您的密钥是真实的、正确生成的、胖且随机的密钥,那么盐就没有用了。另一方面,如果您的密钥是密码,那么您就是真正的密码散列,这将需要更多的努力。

另一方面,IV应该是随机的,而不是密钥的散列。分组密码的适当使用是:

  • 拥有一个唯一的 128 位密钥,由强加密 PRNG生成(例如/dev/urandom在 Linux 系统上)。

  • 加密新消息时,使用强 PRNG 创建一个新的随机 IV(即openssl_random_pseudo_bytes(),它在内部提供/dev/urandom或等效)。

  • 忘记任何“盐”。使用您的主密钥和随机生成的 IV 进行加密。然后,您必须将 IV 与加密消息一起存储(对于 SSN,您最终将得到 32 个字节:IV 为 16 个字节,加密块为 16 个)。

然后适用以下警告:

  • 这是加密;它确保机密性,而不是完整性可以修改部分数据库的人可能会对您的 SSN 造成严重破坏(如果只是在用户之间切换它们)。数据库完整性是一个难题,最知名的解决方案并不好;它们意味着大量开销。

  • 只有在不泄露密钥的情况下,才会保持机密性。但我们设想攻击者获得对服务器内容的异常特权读取访问权限,并且服务器通常知道密钥。如果您将密钥保存在与数据库分开的文件中,那么您可能会获得一些额外的保护,以防止基本的 SQL 注入攻击,这些攻击仅适用于数据库内容;但请注意,您只修复了一些漏洞。

很抱歉打断你——你用这些 SSN 冒着非常危险的机会。让我们根据 PCI-DSS 标准快速定义一些东西:

问:什么是“持卡人数据”?答:持卡人数据是与持卡人相关的任何个人身份数据。这可能是帐号、到期日期、姓名、地址、社会保险号等。与持卡人相关的所有被存储、处理或传输的个人身份信息也被视为持卡人数据。

PCI DSS 是安全信息存储(最好是财务)的标准。规则很严格,不遵守的罚款很高(每月 5000 美元到 100000 美元)。但是,SSN可以根据它进行存储,但同样,这些准则是严格的(本文档的第 14 页)。所以,即使在讨论你当前的实现之前,我强烈建议你知道你正在做什么。

让我们也放下一些东西:

  1. 如果您正在重新传输 SSN(在页面上或其他地方)并且您的传输层不安全,则好像您在后端放置数据的所有加密级别都无关紧要。如今,嗅探非常容易。
  2. 如果你正在设计自己的“加密套件”,因为你认为它会起作用——你还记得那些有漏洞的公司吗?他们认为他们的东西也会起作用。

让我们进入细节。任何不熟悉 PHP(其他答案提供者)的人,该函数mcrypt_encrypt都作为参数:

  • 密码 - 在这里,256 位 AES
  • PK
  • 纯文本
  • 模式(此处为 CBC)

让我们稍微分解一下你的功能。“WTF is this shit”的第一个明显点是您使用 md5 生成密钥和 IV。你知道 MD5 有严重的漏洞,对吧?

第二个明显的 WTF 是密码的选择。AES 512 可用于 PHP。这也会使您的密钥大小加倍。不仅如此,你对盐的使用也很有趣——这就是静脉注射的用途!

第三个明显的 WTF 是为您的私钥使用常量。你确实意识到这个常量会从你的脚本初始化到关闭持续存在,对吧?不仅如此,我敢打赌它是在其中一个文件中以明文形式定义的。这意味着:

  • 如果有人设法在您的网站上找到一个包含/文件读取的vuln,则他们拥有您的私钥
  • 如果有人设法在您的网站上找到一个单一的代码执行漏洞,他们就会拥有您的私钥

考虑安全地存储您的私钥(或者甚至更好,只存储它的散列并要求用户验证密钥才能解密 - 有效地迫使用户知道密钥来解密敏感信息)。

TL;DR:阅读 PCI-DSS,停止编造东西,真正了解您正在使用的工具。我建议从密码学入门书开始 - 对 Bruce Schneier 有任何异议吗?如果没有,请选择实用密码学(或者如果您更喜欢软书籍,Secrets & Lies,假设有比 2001 年更新的版本)。