即使使用Hashed passwordFormat设置,ASP.Net Membership也会将更改的密码保存为纯文本

时间:2009-11-09 12:27:41

标签: asp.net format passwords membership

我正在使用ASP.Net SqlMembershipProvider来管理我的用户。这是我的配置:

<membership defaultProvider="SqlProvider" userIsOnlineTimeWindow="15">
            <providers>
                <clear />
                <add
                    name="SqlProvider"
                    type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
                    connectionStringName="SiteDatabase"
                    applicationName="WPR"
                    minRequiredPasswordLength="6"
                    minRequiredNonalphanumericCharacters="0"
                    enablePasswordRetrieval="false"
                    enablePasswordReset="true"
                    requiresQuestionAndAnswer="false"
                    requiresUniqueEmail="true"
                    passwordFormat="Hashed" />
            </providers>
        </membership>

我的问题是:当我调用Membership.CreateUser来创建新用户时,密码以散列格式存储在数据库中,并带有一个盐 - 这一切都很好。但是,当我在管理功能中调用Membership.ChangePassword时,它以纯文本格式存储密码。我真的无法理解这种行为,因为配置清楚地说“Hashed”并且创建新用户会创建一个哈希密码。

3 个答案:

答案 0 :(得分:6)

在默认ASPMembership提供程序的ChangePassword()方法中,从数据库中检索现有用户的密码格式,该格式是用于为现有用户编码新密码的格式,而不是密码格式在web.config中设置,现在可以指定要使用的其他格式。您可以通过downloading the source code for the default providers自行查看。

我的问题是,对于已经以明文形式存储密码的用户,密码是否以明文形式存储?您可以通过检查表aspnet_Membership中用户的PasswordFormat字段的值来轻松检查此问题。值为:

Clear = 0,
Hashed = 1,
Encrypted = 2,

编辑:

如果您需要自己散列清除密码,框架代码可能会派上用场

// generate a salt
public string GenerateSalt()
{
    byte[] buf = new byte[16];
    (new RNGCryptoServiceProvider()).GetBytes(buf);
    return Convert.ToBase64String(buf);
}

// hashes the password, using the supplied salt
public string HashPassword(string pass, string salt)
{
    byte[] bIn = Encoding.Unicode.GetBytes(pass);
    byte[] bSalt = Convert.FromBase64String(salt);
    byte[] bAll = new byte[bSalt.Length + bIn.Length];
    byte[] bRet = null;

    Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
    Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);

    // this assumes a Hashed password (PasswordFormat = 1)
    HashAlgorithm s = HashAlgorithm.Create( Membership.HashAlgorithmType );
    bRet = s.ComputeHash(bAll);

    return Convert.ToBase64String(bRet);
}

现在您只需从PasswordFormat = 0的数据库中提取所有记录,通过控制台应用程序运行它们以散列密码并将salt,哈希密码保存到数据库,以及将PasswordFormat字段更新为1

答案 1 :(得分:1)

为此,我使用控制台应用程序。我直接在数据库中更改了PasswordFormat表中的aspnet_Membership。然后,我借助ResetPasswordChangePassword方法将密码更改为相同的密码。此外,如果用户被锁定,我会在更改密码之前将其解锁,然后再次锁定。在app.config文件中,我有数据库模型和成员资格提供程序的连接字符串,以及成员资格提供程序定义。

请注意,如果旧密码不符合提供商设置中的最新密码要求,则ChangePassword方法将失败。

代码如下:

static void Main(string[] args)
{
    using (var db = new MyEntities())
    {
        var usersToFix = db.aspnet_Membership.Where(x => x.PasswordFormat == 0).ToList();

        foreach (var userToFix in usersToFix)
        {
            userToFix.PasswordFormat = 1;

            var password = userToFix.Password;
            var passwordQuestion = userToFix.PasswordQuestion;
            var passwordAnswer = userToFix.PasswordAnswer;
            var lastLockoutDate = userToFix.LastLockoutDate;

            db.SaveChanges();


            var user = Membership.GetUser(userToFix.UserId);
            bool locked = user.IsLockedOut;

            if (locked)
            {
                user.UnlockUser();
            }

            var resetPassword = user.ResetPassword();
            user.ChangePassword(resetPassword, password);
            user.ChangePasswordQuestionAndAnswer(password, passwordQuestion, passwordAnswer);

            if (locked)
            {
                userToFix.IsLockedOut = true;
                userToFix.LastLockoutDate = lastLockoutDate;
                db.SaveChanges();
            }

            Console.WriteLine("{0} - OK", user.UserName);
        }
    }


    Console.WriteLine("Done!");
    Console.ReadKey();
}

答案 2 :(得分:0)

Russ的解决方案可能有效,但如果所有现有用户都拥有明确或加密的密码,则可以采用更简单的方法。在web.config中设置2个sql成员资格提供程序,一个使用明确(或加密)密码,另一个使用散列。然后在Web应用程序中的某处执行此代码:

void ConvertPasswordsToHashed()
{
    var clearProvider = Membership.Providers["SqlProvider"];
    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);
    }
}