将密码格式从加密改为哈希

16

我发现关于将现有数据库从加密密码转换为哈希密码的信息非常少。(我能够找到更多有关另一种方式的转换的信息,但它并没有什么帮助。)

正如大多数人所知道的那样,在web.config中更改passwordFormat设置只影响新用户。我有一个包含几百个用户的数据库,我想将他们转换为使用哈希密码,而不改变这些现有密码。

是否有其他人了解如何处理这个问题?感谢任何提示。

4个回答

17

Greg的解决方案是个好的开端,但它不会影响现有用户。SqlMembershipProvider通过在密码表中存储PasswordFormat(0 = clear,1 = Hashed,2 = Encrypted)来保护现有用户和密码。更改提供程序密码格式仅影响对用户表的插入。为了将现有用户的密码转换为Hashed,您必须更改每个条目的PasswordFormat参数。以下是一个简单的方法:

void HashAllPasswords()
{
    var clearProvider = Membership.Providers["SqlProvider_Clear"];
    var hashedProvider = Membership.Providers["SqlProvider_Hashed"];
    int dontCare;
    if (clearProvider == null || hashedProvider == null) return;
    var passwords = clearProvider.GetAllUsers(0, int.MaxValue, out dontCare)
        .Cast<MembershipUser>().ToDictionary(u => u.UserName, u => u.GetPassword());

    using (var conn = new SqlConnection(
           ConfigurationManager.ConnectionStrings[0].ConnectionString))
    {
        conn.Open();
        using (var cmd = new SqlCommand(
               "UPDATE [aspnet_Membership] SET [PasswordFormat]=1", conn))
            cmd.ExecuteNonQuery();
    }

    foreach (var entry in passwords)
    {
        var resetPassword = hashedProvider.ResetPassword(entry.Key, null);
        hashedProvider.ChangePassword(entry.Key, resetPassword, entry.Value);
    }
}

感谢您的晚回复。事实上,这个任务仍然在我的后勤计划中,我并没有特定的技巧。我会在有时间的时候深入探索这段代码。 - Jonathan Wood
在获取所有用户密码时,您应该过滤锁定的用户。否则,在密码重置/更改阶段会失败。 - Denis The Menace
更改为:var passwords = clearProvider.GetAllUsers(0, int.MaxValue, out dontCare).Cast<MembershipUser>().Where(u => u.IsLockedOut == false && u.IsApproved == true).ToDictionary(u => u.UserName, u => u.GetPassword()); - Jaime García Pérez
顺便说一下,最近的.NET更新强制您使用哈希密码或忍受不断的关键错误,因为加密=糟糕。 - Jack

13
这是我想到的方法,可以先试着实现一下:
  1. 在web.config中创建两个MembershipProviders,一个用于加密密码,另一个用于哈希密码。
  2. 使用加密密码提供程序循环遍历所有用户。 (SqlMembershipProvider.GetAllUsers)
  3. 使用加密密码提供程序获取用户的密码。 (MembershipUser.GetPassword)
  4. 使用哈希密码提供程序将用户的密码更改为相同的密码。 (MembershipUser.ChangePassword)
所以就像这样:
    <membership defaultProvider="HashedProvider">
        <providers>
            <clear />
            <add name="HashedProvider" connectionStringName="MembershipConnectionString" enablePasswordRetrieval="false"  requiresQuestionAndAnswer="false" applicationName="MyApp" passwordFormat="Hashed"  type="System.Web.Security.SqlMembershipProvider" />
            <add name="EncryptedProvider" connectionStringName="MembershipConnectionString" enablePasswordRetrieval="true" requiresQuestionAndAnswer="false" applicationName="MyApp" passwordFormat="Encrypted" type="System.Web.Security.SqlMembershipProvider" />
        </providers>
    </membership>

代码:

SqlMembershipProvider hashedProvider = (SqlMembershipProvider)Membership.Providers["HashedProvider"];
SqlMembershipProvider encryptedProvider = (SqlMembershipProvider)Membership.Providers["EncryptedProvider"];

int unimportant;
foreach (MembershipUser user in encryptedProvider.GetAllUsers(0, Int32.MaxValue, out unimportant ))
{
    hashedProvider.ChangePassword(user.UserName, user.GetPassword(), user.GetPassword());
}

1
我目前正在尝试将明文密码更新为加密密码。我想要它们被散列,但客户不同意。无论如何,这个解决方案看起来很有前途。它的简洁性非常优雅。希望它能够奏效。 - mikesigs
2
我让它工作了,但是注意事项太多了。首先,你不能简单地调用哈希提供程序上的ChangePassword,因为你必须提供现有密码(它试图进行散列以便与未散列的数据库值比较)。所以,你必须调用ResetPassword来获取临时的散列密码,以便调用ChangePassword。性能瓶颈是致命的。你需要检索每个MembershipUser,然后对每个用户都调用GetPassword、ResetPassword和ChangePassword。超过3*N次数据库查询!我将其包装在一个事务中,很快就耗尽了我的连接池。 - mikesigs
我打算调查一下CLR存储过程,这样我就可以从SQL中调用提供程序的加密机制。如果成功的话,我会回复帖子的。 - mikesigs
2
我终于有点时间来尝试一下这个。让我说,我并不完全理解这种方法的细节,但我在单个帐户上尝试了一下。它可以编译和运行良好,并且ChangePassword()返回true。然而,它似乎没有更新数据库。在aspnet_Membership表中,测试帐户的PasswordFormat仍然显示为2。我错过了什么吗? - Jonathan Wood
1
这似乎不起作用,即使您通过“哈希”提供程序更改密码,我仍然会继续保留passwordFormat。 - Roman Korneev

1

出于安全考虑,在数据库中从加密密码转换为哈希值绝对是正确的决定。

通常,要将现有的加密密码转换为哈希值,您应该首先解密它们,然后再进行哈希。请注意,您将失去(在最终切换时)原始密码。相反,您将获得用户密码的唯一指纹(哈希)。

还要考虑使用盐来进行哈希(防御彩虹表等攻击),并且还要查看慢哈希算法,例如BCryptCodeplex文章:如何安全地存储密码)而不是像MD5这样的快速算法,以确保安全性。

请记住,将来更改哈希算法的工作量要比从加密更改哈希算法大得多。因此,您希望第一次就做对它;)


是的,我了解什么是散列密码以及你无法将其转换回原始密码。我知道需要先解密加密的密码,然后再进行哈希。但我不知道如何对所有记录执行此操作,以便与ASP.NET成员身份验证一起使用。(顺便说一下,ASP.NET成员身份验证使用盐来进行密码哈希吗?) - Jonathan Wood
1
ASP.NET会员的默认哈希算法是SHA1,它们使用Base64编码的盐。 - Martin Buberl

0

我建议你不要随意哈希密码,因为这种方法有很多注意事项。对我来说,关于哈希密码的博客文章非常有见地,我认为你应该阅读一下。为什么你想要哈希密码而不是加密?


3
谢谢提供链接,它是一篇有趣的阅读。话虽如此,没有一种方法是100%安全的,而哈希密码是当前密码存储的行业标准。我为了方便用户(可以通过电子邮件发送)设置了我的网站使用加密密码,但我收到了一些抱怨,称这会给我的用户带来风险,特别是当他们在多个网站上使用相同的密码时。 - Jonathan Wood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接