Why Change your passwords to Salted Hashes
I’ve had a site that for a long time has been on my TODO list to convert the site from using plaintext passwords to using passwords that are hashed and salted. No matter what the excuse, the truth is, it’s not really just your data that you’re protecting by encrypting your passwords, but it’s the user, since many still use the same password on many systems.
Our Conversion Steps
1. Build a command line application to convert all passwords in our SQL server to hashed data with salt.
2. Change current code in the site when a user has forgotten their password.
3. Change current code in site for when a new user is created.
4. Change current code in site for when a user wants to change their password.
5. Change the code for user login to except the plaintext password they enter, hash/salt it and compare it to what’s in the DB
Advice & Example sites:
While doing research on hashing and what/how to do it, I’m learned a couple things. 1. Don’t write your own encryption (You are not an expert!) 2. If you can decrypt the passwords back to plaintext then someone else probably can (in our code we will never be decryption but we’ll take what the user says is their password and encrypt it and compare it to what’s in the database) 3. Do not use MD5, while one of our example sites has the option, it’s just for example and for the same reason always salt your hashes (see: https://crackstation.net/ )
The 2 sites that I used to for code samples are:
1. The how to for salted hashes How to hash data with salt in C# or VB.NET
Converting All Current Passwords
To convert our current passwords from Plaintext to salted hash passwords we’ll need to build a small command line application to loop through the SQL table, encrypt, created the hash and salt it and then write it to the sql. I created a new column (nvarchar(150)) next to my current password column (so I could test logins) Also I shouldn’t have to say but do this in a test DB.
Using the code samples from Site 1 listed above lets first create an array or list of all current users with their passwords
sub getpasswords() Dim strSQL As String Dim sourceids As String = Nothing Dim conn1 As New SqlConnection("Your Server Connection") strSQL = "SELECT * FROM tblLogIn" conn1.Open() Dim newPass As New List(Of PasswordStruct) Dim kmd As New SqlCommand(strSQL, conn1) Dim r As SqlDataReader r = kmd.ExecuteReader() 'loop through table and add each user/password to a list If r.HasRows Then While r.Read() newPass.Add(New PasswordStruct(r("UserID"), r("USerName"), r("Password"))) End While End If Dim PasswordCollection As ICollection = newPass For Each s As PasswordStruct In PasswordCollection 'starts the function ComputeHash with password, userid, type of hash algorithm (choose one) and saltbytes ComputeHash(s.Password, s.UserID, "SHA256", Nothing) Next End sub
using the sample code for ComputeHash (see site #1 above), adding the argument for the userid and add this code to write each new hash to the DB
Dim strSQL As String 'Dim conn As SqlConnection Dim sourceids As String = Nothing Dim conn1 As New SqlConnection("your sql connection") 'update since records already exist strSQL = "UPDATE tblLogIn SET NewPassword=@computehash where userid =@userid Dim objkmd As New SqlCommand(strSQL, conn1) objkmd.CommandType = CommandType.Text <pre>objkmd.Parameters.Add("@computehash", Convert.ToString(Request.QueryString("uCode"))) <pre> objkmd.Parameters.Add("@userid", Convert.ToString(Request.QueryString("UserId"))) Try conn1.Open() objkmd.ExecuteNonQuery() Catch ex As Exception Finally objkmd.Dispose() conn1.Close() conn1.Dispose() End Try
You now have a column in your table with all the current users passwords hashed and salted.Forgotten Passwords
Forgotten, New and Changing Passwords
If your site is currently using plaintext passwords, odds are that when a user forgets their password, you are sending them their password in plaintext from the DB to their email address. Now since we’ve hashed and salted the new passwords, if you sent the user the long string of code in the DB, this wouldn’t help them login to the site. Our only option is to have the user change their password. We’ll email the user a link to the new password page, allow them to add a new password, from there we’ll do the same we did above, take the plaintext password and encrypt and salt it.
For this section we’ll be using the sample code on site #1 and site #2 above. Since you probably already have modules for the changing passwords or creating passwords for new users, I will not be adding code here, but telling you an outline of what you need to do.
Following the lead on example code site #2 , create your forgotten password page or add a panel on your current change password screen with the email fields and the password fields laid out on the website. Since the site doesn’t dive into if you have encrypted passwords, here is where you’ll need to make a change.
Using example code site #1, you’ll want to make a class for your own site, by making this code a class, you’ll be able to reuse the code not only for forgotten passwords but also for new user passwords and users changing their passwords.
Now once the user follows the long link provided in their email to change their password, they’ll input their new password and you’ll call ComputeHash with the arguments of the plaintext password they supplied, the hashAlgorithm and saltbytes.
Fine, lets do it
Below is the code from Example site #2 with changes for computing the hash, be sure to update the code with your table and column names.
Protected Sub btnChangePwd_Click(sender As Object, e As System.EventArgs) Handles btnChangePwd.Click Try Dim thepwdhash as string thepwdhash = SimpleHash.ComputeHash(txtNewPwd.Text.Text, "SHA256", Nothing) 'added code ' Here we will update the new password and also set the unique code to null so that it can be used only for once. cmd = New SqlCommand("update Tbl_Login set UniqueCode='',Pwd=@pwd where UniqueCode=@uniqueCode and (EmailId=@emailid or UserName=@username)", con) cmd.Parameters.AddWithValue("@uniqueCode", Convert.ToString(Request.QueryString("uCode"))) cmd.Parameters.AddWithValue("@emailid", Convert.ToString(Request.QueryString("emailId"))) cmd.Parameters.AddWithValue("@username", Convert.ToString(Request.QueryString("uName"))) cmd.Parameters.AddWithValue("@pwd", thepwdhash) ' changed pwd is the new hash instead of the plaintext that the user supplied If con.State = ConnectionState.Closed Then con.Open() End If cmd.ExecuteNonQuery() lblStatus.Text = "Your password has been updated successfully." txtNewPwd.Text = String.Empty txtConfirmPwd.Text = String.Empty Catch ex As Exception lblStatus.Text = "Error Occured : " & ex.Message.ToString() Finally cmd.Dispose() con.Close() End Try End Sub
The same can be done with your module for New User Password because there isn’t a password and you’re writing a new one, like above where we don’t care what the password was, we’re just writing a new one. It changes a bit for Changing Passwords.
When changing passwords, the difference is, we want to check the “old” password before we change the new password. You’ve probably already have code with textboxes that have one box for old password, 2 boxes for new password and a submit button.
Once the submit button is pressed we need to check the old password first. IF the old password matches, we can then once again compute the hash and write it over the old password
Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnSubmit.Click If Page.IsValid Then 'check old password Dim OldPassTrue As String OldPassTrue = CheckOldPassword(PSW.Text) If OldPassTrue = "True" Then ComputeHash(PSW1.Text, lblEmail.Text, "SHA256", Nothing) 'then write the new password to the password column Else lblUserMessages.Text = "<br>Your old password does not match." lblUserMessages.Visible = "true" End If End If End Sub Function CheckOldPassword(ByVal OldPass As String) As Boolean Dim strSQL As String Dim objConn As SqlConnection Dim objkmd As SqlCommand Dim OldPassGood As String = Nothing strSQL = "SELECT NewPassword FROM tblLogin" strSQL += " WHERE UserName = '" & username & "'" objConn = New SqlConnection(ConfigurationManager.AppSettings("conn")) objkmd = New SqlCommand(strSQL, objConn) objkmd.CommandType = CommandType.Text Try objkmd.Connection.Open() Dim dr As SqlDataReader = objkmd.ExecuteReader(CommandBehavior.CloseConnection) If dr.HasRows() Then dr.Read() Dim passwordHashSha256 As String = dr("NewPassword") OldPassGood = VerifyHash(OldPass, "SHA256", passwordHashSha256).ToString() End If dr.Close() Catch ex As Exception Response.Write(ex.ToString()) Finally objkmd.Connection.Close() objkmd.Dispose() End Try return OldPassGood End Function
Last on our list of TODOs, users need to be able to login. If you’re currently using plaintext passwords, you’re just checking against the DB is the username and password match. We now need to (again) compute the hash for what the say is the password and then compare it against what’s in the DB, if the hashes match they the user is allowed in the site. You notice that we never decrypt the hash but just compare.
I added to my existing login code, after the user hit submit to login to the site
Dim DoesPasswordPass As Boolean DoesPasswordPass = checkpassword(pass, user) 'if DoesPasswordPass = "True" then the user is allowed in the site
Public Shared Function checkpassword(ByVal password As String, ByVal user As String) Dim passwordHashSha256 As String Dim strSQL As String Dim PasswordPass As Boolean Dim sourceids As String = Nothing Dim conn1 As New SqlConnection(ConfigurationManager.AppSettings("readconn")) 'readonly strSQL = "SELECT NewPassword FROM tbllogin where userName= @userName" conn1.Open() Dim kmd As New SqlCommand(strSQL, conn1) kmd.Parameters.Add("@UserName", SqlDbType.VarChar, 50).Value = user passwordHashSha256 = kmd.ExecuteScalar().ToString() conn1.Dispose() kmd.Dispose() PasswordPass = VerifyHash(password, user, "SHA256", passwordHashSha256).ToString()</pre> 'password is what the user thinks is their password, SHA256 is hash type, Return PasswordPass is what the current hash is Return PasswordPass End Function