ASP.Net Membership saves changed password as plain text even with Hashed passwordFormat set

半世苍凉 提交于 2019-12-18 18:13:16

问题


I'm using the ASP.Net SqlMembershipProvider to manage my users. Here is my config:

<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>

My problem is this: when I call Membership.CreateUser to create new users, the password is stored in the DB in hashed format with a salt - which is all good. However, when I call Membership.ChangePassword in an admin function, it is storing the password in plain text format. I really cannot understand this behaviour, since the config clearly says "Hashed" and creating a new user creates a hashed password.


回答1:


Within the ChangePassword() method of the default ASPMembership provider, the password format for an existing user is retrieved from the database and is the format used to encode a new password for an existing user, and not the password format that is set in web.config, which may now specify a different format to use. You can see this for yourself by downloading the source code for the default providers.

My question is then, is the password being stored in clear text for a user who already had a password stored in clear text? You can check this easily by checking the value of the PasswordFormat field for the user in table aspnet_Membership. The values are:

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

EDIT :

if you need to hash clear passwords yourself, the framework code may come in handy

// 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);
}

now you just need to pull all records from the database where the PasswordFormat = 0, run them through a console app to hash the password and save the salt, hashed password to the database, as well as update the PasswordFormat field to 1




回答2:


To do that I use a console application. Directly in the database I change PasswordFormat in the aspnet_Membership table. Then I change the password to the same with the help of ResetPassword and ChangePassword methods. Also if the user was locked out I unlock it before changing the password and then lock again. In the app.config file I have connection strings for database model and membership provider, as well as the membership provider definition.

Note, that if the old password does not satisfies your latest password requirements in the provider settings then the ChangePassword method would fail.

The code is the following:

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();
}



回答3:


Russ's solution probably works, but there's a simpler way if all your existing users have either clear or encrypted passwords. Set up 2 sql membership providers in your web.config, one using clear (or encryped) passwords and another using hashed. Then execute this code somewhere within your web application:

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);
    }
}


来源:https://stackoverflow.com/questions/1700702/asp-net-membership-saves-changed-password-as-plain-text-even-with-hashed-passwor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!