开发者

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

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.


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


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


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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜