Unable to verify bcrypt-hashed password from mysql with vb.net

1k Views Asked by At

I have an application in vb.net using mysql as database. The application has a login form. There is also a registration form that can enter a new password in the archive using bcrypt.net, as shown at this site:

http://derekslager.com/blog/posts/2007/10/bcrypt-dotnet-strong-password-hashing-for-dotnet-and-mono.ashx

This is my code for registering a new user (public procedure Clean is for clean the forms):

 Private Sub btSignUp_Click(sender As Object, e As EventArgs) Handles btSignUp.Click
dim hash as string
        hash = HashPassword(txConfirmPass.Text)
        If (txPass.Text <> txConfirmPass.Text) Then
            MessageBox.Show("Passwords don't matches")
        Else
            Dim sql As String = "INSERT INTO fusion_login(name,user,password) VALUES (@name,@user,@pass)"
            Using myconnection As MySqlConnection = Connection.getInstance.getConnection()
                Using mycommand As New MySqlCommand()
                    With mycommand
                        .CommandText = sql
                        .CommandType = CommandType.Text
                        .Connection = myconnection
                        .Parameters.Add("@name", MySqlDbType.VarChar).Value = txName.Text
                        .Parameters.Add("@user", MySqlDbType.VarChar).Value = txUser.Text
                        .Parameters.Add("@pass", MySqlDbType.VarChar).Value = hash
                    End With
                    Try
                        myconnection.Open()
                        mycommand.ExecuteNonQuery()
                        If (MessageBox.Show("Do you insert a new user again?", "Register", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes) Then 
                            Clean(Me)
                        Else
                            Me.Hide()
                            Login.Show()
                        End If
                    Catch ex As MySqlException
                        MessageBox.Show("Error: " + ex.ToString)
                    Finally
                        myconnection.Close()
                    End Try
                End Using
            End Using
        End If
    End Sub

This code works!

Now I'm trying to implement the code that authenticates a user created using the above procedure, but I can't get it to work. Here is what I have so far:

      Private Sub btLogin_Click(sender As Object, e As EventArgs) Handles btLogin.Click
Dim hash as String
hash = HashPassword(txPass.text)
                Dim sql As String = "SELECT  user,password FROM fusion_login WHERE user = @user AND password = @pass"
                Using myconnection As MySqlConnection = Connection.getInstance.getConnection()
                    Using mycommand As New MySqlCommand()
                        With mycommand
                            .CommandText = sql
                            .CommandType = CommandType.Text
                            .Connection = myconnection
                            .Parameters.Add("@user", MySqlDbType.VarChar).Value = txUser.Text
                            .Parameters.Add("@pass", MySqlDbType.VarChar).Value = hash
                        End With
                        Try
    myconnection.Open()
                        myreader = mycommand.ExecuteReader
                        If myreader.HasRows = 0 Then
                            Me.Hide()
                            FusionPrincipal.Show()
                        Else
                            MessageBox.Show("Error", "Login", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                            txUser.Focus()
                        End If

                        Catch ex As MySqlException
                            MessageBox.Show("Error: " + ex.ToString)
                        Finally
                            myconnection.Close()
                            myreader.Close()
                        End Try
                    End Using
                End Using

I'm not sure whether I'm inserting the password wrong, or comparing the username/password wrong at login. What could be the problem here?

2

There are 2 best solutions below

5
Joel Coehoorn On

The problem is this line:

If myreader.HasRows = 0 Then

In this code, myreader.HasRows is Boolean, but 0 is an integer. To make this comparison, the 0 value must first be implicitly cast to a boolean. Casting 0 to a boolean results in False. This means the code will only enter that If block if the data reader did not return any results. I believe this is the opposite of what you want to happen. Instead, just do this:

If myreader.HasRows = True Then

or, even better:

If myreader.HasRows Then

While I'm here, it's not enough to just hash the password. You also need to set a salt value for each user, and combine that with the password before creating the hash. When you authenticate the user, you much retrieve the salt for that username, combine it with the supplied password, and then compare it to the result. The password comparison code should look more like this:

Public Function AuthenticateUser(ByVal Username As String, ByVal Password As String) As Boolean
    Dim sql As String = "SELECT  user,password,salt FROM fusion_login WHERE user = @user"
    Using myconnection As MySqlConnection = Connection.getInstance.getConnection(), _
          mycommand As New MySqlCommand(sql, myconnection)

        Try
            mycommand.Parameters.Add("@user", MySqlDbType.VarChar).Value = Username
            myconnection.Open()
            Using rdr As MySqlDataReader = mycommand.ExecuteReader()
                If Not rdr.Read() Then Return False
                Return HashPassword(Password, rdr("salt")).Equals(rdr("password"))
            End Using
        Catch 'You probably want to do more here, but "Return False" was enough for this quick example
             Return False
        End Try
    End Using
End Function

Of course, you'll also need to update the HashPassword() function to allow the additional parameter, your insert code to generate the salt in the first place, and your database table to have space to store the value.

One more thing... even this isn't totally correct. If you really want to do it right, you need to ensure that the plaintext password is only ever held in memory with a SecureString object.

0
Tiago NET On

Have found the solution for this case:

If mydatareader.Hasrows Then
   While mydatareader.Read
       If (Bcrypt.Net.Bcrypt.Verify(txtPassword.text, mydatareader.Item("password"))) then
           MsgBox("Valid Credentials")
       else
           MsgBox("Invalid Credentials")
       End if
   End While
  mydatareader.Close()
End If