16

I'm finding surprisingly little information on converting an existing database from Encrypted passwords to Hashed passwords. (I was able to find a bit more information on converting the other way, but it wasn't of much help.)

As most people know, changing the passwordFormat setting in web.config only affects new users. I have a database with a couple of hundred users and I'd like to convert them to use hashed passwords without changing those existing passwords.

Is anyone else familiar with how one might approach this? Thanks for any tips.

Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466

4 Answers4

17

Greg's solution is a good start, but it won't affect existing users. The SqlMembershipProvider protects existing users and passwords by storing the PasswordFormat (0=clear, 1=Hashed, 2=Encrypted) in the table along with passwords. Changing the provider password format only affects inserts to the user tables. In order to convert existing users' passwords to Hashed, you have to change the PasswordFormat parameter for each entry. Here is a simple way to do this:

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);
    }
}
Dan Walton
  • 171
  • 1
  • 2
  • Thanks for the late reply. In fact, this task is still on my backburner and I really didn't have a specific technique. I will explore this code deeper when I get some time. – Jonathan Wood Mar 15 '12 at 04:45
  • When getting all users' passwords you should filter locked out users. Otherwise it will fail on password reset/change stage. – Denis The Menace Apr 04 '13 at 05:48
  • Chage this : var passwords = clearProvider.GetAllUsers(0, int.MaxValue, out dontCare) .Cast().Where(u=> u.IsLockedOut==false && u.IsApproved==true).ToDictionary(u => u.UserName, u => u.GetPassword()); – Jaime García Pérez May 12 '15 at 16:42
  • btw, the most recent .NET update forces you to use hashed passwords or endure nonstop critical errors that Encryption = Bad. – Jack Nov 27 '17 at 17:24
13

This is the approach I'd start with to see how far I got:

  1. Create two MembershipProviders in my web.config, one for encrypted passwords and one for hashed.
  2. Loop through all users using encrypted password provider. (SqlMembershipProvider.GetAllUsers)
  3. Get the user's password using encrypted password provider. (MembershipUser.GetPassword)
  4. Change the user's password to the same password using hashed password provider. (MembershipUser.ChangePassword)

So it'd be something like this:

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

code:

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());
}
Greg
  • 16,540
  • 9
  • 51
  • 97
  • 1
    I'm currently trying to update clear text passwords to encrypted passwords. I want them hashed but the client does not. Anyways, this solution looks promising. Very elegant in its simplicity. Hope it works. – mikesigs Feb 10 '11 at 15:36
  • 2
    I got it working but the caveats were too much to bear. First of all, you cannot simply call ChangePassword on the hashed provider because you must supply the existing password (which it attempts to hash in order to compare to the db value which isn't hashed). So you have to call ResetPassword to get a temporary hashed password in order to call ChangePassword. The performance is the killer. You need to retrieve each MembershipUser, then call GetPassword, ResetPassword, and ChangePassword for each. Over 3*N hits on the db! I wrapped it in a transaction and quickly exhausted my conn pool. – mikesigs Feb 11 '11 at 14:54
  • I'm going to investigate a CLR stored procedure so I can call the provider's encryption mechanisms from SQL. I'll post back if that works. – mikesigs Feb 11 '11 at 14:56
  • 2
    I finally got a bit of time to play with this. Let me say that I don't fully understand the details of this approach, but I tried it on a single account. It compiles and runs fine, and ChangePassword() returns true. However, it doesn't appear to update the database. It still shows the PasswordFormat is 2 for the test account in the aspnet_Membership table. Did I miss something? – Jonathan Wood Feb 16 '11 at 02:44
  • 1
    This look's like not works, I still continue keep passwordFormat even though you change password through "hashed" provider – Roman Korneev May 22 '15 at 17:50
1

For security reasons, it's definitely the right decision to switch from encrypted passwords to hashes in your database.

Generally to create hashes out of your existing encrypted passwords, you should first decrypt them and then hash them. Be aware that you will loose (when you finally switch) the original passwords. Instead you're going to have a unique fingerprint (hash) of the users passwords.

Think also about using salt for the hashing (defense against rainbow tables etc.) and also have a look in slow hashing algorithms like BCrypt (Codeplex & Article: How To Safely Store A Password) for security reasons instead of fast ones like MD5.

Keep also in mind, that it will be way more effort to switch the hashing algorithm in the future than changing it from ecryption to hash. So you want to do it right the first time ;)

Martin Buberl
  • 45,844
  • 25
  • 100
  • 144
  • Yes, I understand what hashed passwords are and how you cannot convert from hashed to the original password. And I know I need to decrypt the encrypted passwords and then hash them. What I don't know is how I can do this for all records in a way that will then work with ASP.NET membership. (BTW, doesn't ASP.NET membership use a salt for hashed passwords?) – Jonathan Wood Feb 09 '11 at 18:43
  • 1
    ASP.NET membership's default hash algorithm is SHA1 and they use salt encoded as Base64. – Martin Buberl Feb 09 '11 at 18:48
0

I would caution you against hashing your passwords haphazardly since there are a lot of caveats to that approach. This Blog post about hashing passwords was very insightful to me, and I think that you should read it. Why do you want hashed passwords instead of encrypted?

Jeffrey Greenham
  • 1,382
  • 5
  • 16
  • 33
  • 3
    Thanks for the link, it was an interesting read. That said, no method is 100% secure, and hashing passwords is the current industry standard for password storage. I had set up my site to use encrypted passwords as a convenience to users (I can email it to them), but I got complaints that this posed a risk to my users, especially when they use the same password on multiple sites. – Jonathan Wood Feb 09 '11 at 18:41