.Net Convert Your Site From PlainText Passwords to Salted Hashes

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

2. How to recover and reset the forgot/lost password in asp.net using activation link in email id

 

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.

Forgotten Passwords

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.

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

 

User Login

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

 

 

 

 

 

 

Technorati Tags: , , , , , ,

     

Leave a Reply