0

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

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

计划是:

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

  2. 当新用户注册时:

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

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

  3. 用户登录后:

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

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

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

我是新手,以前曾使用过企业库安全性来处理这类东西(.NET 核心中不再提供它!),所以任何帮助都将不胜感激!

4

2 回答 2

2

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

我不清楚第 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));
}

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

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,那么您可能需要跨平台解决方案)
于 2016-07-17T00:41:54.650 回答
1

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

基本上,你有一个主密钥——一个对称密钥,或者可能是 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)限制单个密钥用于加密的数据量(因为从技术上讲,您可以使用单个密钥加密多少数据并且在数学上仍然是“安全的”)。

于 2016-07-17T18:32:49.220 回答