.NET DPAPI 和 AES 加密:感知检查

Posted

技术标签:

【中文标题】.NET DPAPI 和 AES 加密:感知检查【英文标题】:.NET DPAPI and AES Encryption: Sense-Check 【发布时间】:2016-07-16 23:17:56 【问题描述】:

我正要为我目前正在开发的网站编写一个新的加密系统,如果可能的话,我想看看我是否可以在开始之前让某人对其进行感应检查!

更新: 我原来的问题应该更清楚。我需要加密一些用户数据,我还需要能够在以后的数据中读取这些数据。而且,我还需要存储用户密码或密码的哈希值,以便在登录时验证用户。

计划是:

    主密钥:创建一个 DPAPI 密钥设置器应用程序以获取基于文本的主密钥,通过 DPAPI 进行加密,然后将加密的输出保存到服务器上的文本文件中。这是我每次将站点移动到新服务器时都会执行的一次性任务。主密钥将用于执行 AES 加密。

    新用户注册时:

    2.1。保存密码数据/密码数据的哈希。

    2.2。加载主密钥文件,使用 DPAPI 解密密钥。使用解密的主密钥和每个用户数据的新随机 IV 来创建 AES 加密字符串。通过在加密字符串前加上相应的随机IV来保存每个加密字符串,并插入到数据库中的varchar列中。

    用户登录时:

    3.1。匹配密码哈希以验证用户。

    3.2。对于每个加密的用户数据字段,将内容分成两部分:IV 和加密数据。从 DPAPI 和 IV 中获取主密钥,解密数据并显示在屏幕上。

听起来怎么样?以上有什么明显的缺陷吗?

我是新来的,过去曾使用 Enterprise Library Security 来处理这类东西(.NET core 中不再提供它!),所以任何帮助都将不胜感激!

【问题讨论】:

散列通常比加密密码更好。 不要加密密码,当攻击者得到数据库时,他也会得到加密密钥。使用随机盐在 HMAC 上迭代大约 100 毫秒,然后将盐与哈希一起保存。使用 password_hash、PBKDF2、Bcrypt 等函数和类似函数。关键是让攻击者花费大量时间通过蛮力寻找密码。有关更多信息,请参阅@bartonjs 的答案。 我知道使用散列是个好主意,但在这种情况下 - 黑客不会从数据库中获取加密密钥,因为密钥本身将使用 DPAPI 加密。话虽如此,我还是打算使用密码散列,并将 DPAPI + aes 用于非密码用户字段 【参考方案1】:

如果可以避免的话,你永远不应该存储密码。

我不清楚第 1 步中的密码是什么。如果新用户直到第 2 步才出现,这是谁的密码?

无论如何,对于您的用户来说,更好的计划是使用/保存衍生材​​料。例如,在新用户注册时执行类似

byte[] exportBytes;
byte[] exportSalt;
int exportPasswordSettingsVersion = YourSystemConfiguration.NewPasswordSettingsVersion;

using (Rfc2898DeriveBytes registerer = new Rfc2898DeriveBytes(
    newUserPassword,
    YourSystemConfiguration.GetSaltSize(exportPasswordSettingsVersion),
    YourSystemConfiguration.GetIterationCount(exportPasswordSettingsVersion)))

    exportSalt = registerer.Salt;

    exportBytes = registerer.GetBytes(
        YourSystemConfiguration.GetDerivedKeySize(exportPasswordSettingsVersion));

然后您导出盐字节(为您随机生成)、派生的密码字节、作为用户配置文件一部分的 sett。当用户登录时,您加载这些值并检查它们是否匹配:

using (Rfc2898DeriveBytes verifier = new Rfc2898DeriveBytes(
    inputPassword,
    loadedProfile.Salt,
    YourSystemConfiguration.GetIterationCount(loadedProfile.PasswordSettingsVersion)))

    byte[] verifyBytes = registerer.GetBytes(loadedProfile.PasswordVerify.Length);

    if (!ConstantTimeEquals(verifyBytes, loadedProfile.PasswordVerify))
    
        return false;
    

    if (loadedProfile.PasswordSettingsVersion < YourSystemConfiguration.GetIterationCount(exportPasswordSettingsVersion))
    
        // Re-derive their password and save it with your newer (stronger, presumably) cryptographic settings.
    

    return true;

这个方案:

使用 RFC2898 的 PBKDF2 算法从密码中派生密钥,存储此派生值比存储密码更好,因为如果您的凭据数据库遭到破坏,它不会泄露密码。 为每个新用户使用一个新的随机盐。 (也可以在密码更改或升级登录时重新生成) 保存(并加载)有关设置密码时使用的设置的信息,以便您以后更改默认设置。 不使用 DPAPI(DPAPI 还不错,但仅适用于 Windows,如果您使用的是 .NET Core,则可能需要跨平台解决方案)

【讨论】:

抱歉,我最初的问题不是很清楚。我已经更新了它。感谢提供代码示例,非常有用。 另外,关于哈希,您认为将盐/哈希/迭代次数保存在一个 blob 中以存储在单个数据库列中是否有任何危害? 同样,您是否发现存储 IV 以及非密码用户数据字段的加密 AES 字符串有什么危害? 通常这些东西被添加到哈希之前并分别加密,它们不是秘密。【参考方案2】:

忽略密码问题,只考虑用户数据,你的方案很好,但可以改进。

基本上,您有一个主密钥——一个对称密钥,或者可能是 16 或 32 个字节,它受到静态保护(在磁盘上),然后您在服务器上的内存中对其进行解密。

用户数据使用主密钥加密,每条数据都有一个随机 IV。请务必使用加密性强的随机数据。

您将 IV 和密文存储在一起,这很好。虽然 IV 和密文是二进制的,所以您要么必须使用 blob 数据库类型,要么使用 Base64 对二进制进行编码以将其存储为 varchar。

您应该考虑添加某种形式的篡改检测。例如,您可以使用两个主密钥——一个用于加密/解密,一个用于数据的 HMAC。您将使用加密密钥加密数据,然后在 IV + 密文上应用 HMAC(使用 HMAC 的第二个主密钥)并将 HMAC 与 IV 和密文一起存储(您甚至可以附加它)。在解密之前,您验证 HMAC。这会告诉您 IV + 密文中是否有任何内容被更改。

你没有提到填充。如果使用 AES CBC 模式,如果数据不是 16 字节的倍数,则需要填充数据。使用填充,您只需要小心不要意外提供“填充预言”,攻击者可以在其中向服务器发送任意密文,服务器的响应会告诉攻击者“填充错误”或“无效解密” " -- 即服务器响应不同。

如果您使用 AES GCM 模式,则内置 HMAC 等效项 - 您只需要单个主密钥,GCM 本身将检测密文的篡改。这还允许您包含不属于加密的“关联数据”,但包含在“身份验证”中,即就像它包含在 HMAC 中一样。例如,用户名“Joe”可以作为关联数据包含在 Joe 数据的 GCM 加密中。那么,GCM模式在解密数据时,不仅会检测到IV+密文被篡改,而且用户名还必须是“Joe”——注意这里“Joe”没有加密。

个人IV,每次随机,对于每条数据,是一个非常好的主意。您可能还会考虑在更换主密钥之前使用了多长时间。它应该有一个有限的生命周期,当它被“旋转”、“滚动”或“重新加密”(所有的意思都是一样的——你正在替换它)时,你需要某种方法来重新加密所有内容。您希望定期(可能是 3 个月,也可能是一年)更改主密钥,以 1)限制万一主密钥被泄露(例如仅 3 个月)和 2)限制暴露的数据量单个密钥用于加密的数据量(因为从技术上讲,您可以使用单个密钥加密多少数据并且在数学上仍然是“安全的”)。

【讨论】:

@zaph OP 描述了使用 DPAPI 保护密钥。这将取决于 Windows——它可以绑定到 Windows 用户 ID(即安全主体)或机器。这超出了这个问题的范围,DPAPI 如何保护密钥。谷歌“Windows 数据保护 API”了解详情。 了解,评论已删除。 @JimFlood 感谢您的全面回答。我打算使用 RijndaelManaged().GenerateIV() 来创建随机 IV。由于 .NET 中没有 AES HCM 模式(在撰写本文时),我可能会使用 AES CBC 并采用您的 HMAC 和两键建议。我发现了一个例子,有人在做非常相似的事情:link to Github。我可能会以此为基础,切换到 RijndaelManaged 并进行一些修改以添加配置版本控制,以便以后轻松更改盐大小/迭代

以上是关于.NET DPAPI 和 AES 加密:感知检查的主要内容,如果未能解决你的问题,请参考以下文章

带有 ASP.NET 服务帐户的 DPAPI

使用 DPAPI 保护加密密钥:明显的漏洞?

SessionSecurityTokenHandler 尝试使用 DPAPI 解密 RSA 加密 cookie 中的 SessionSecurityToken;为啥?

AES 加密和解密

Java如何检查值已经AES加密;

在 Python 中使用 DPAPI?