1

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?

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
Tiago NET
  • 153
  • 9
  • This is a good start -- you got farther than most by at least choosing to hash and using bcrypt rather than something less secure. Unfortunately, you're still failing to choose and store a salt value for each user. – Joel Coehoorn Mar 23 '15 at 01:35

2 Answers2

1

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.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • I've download dll's for the site above and used with reference in project. In header the project: Imports BCrypt.Net.BCrypt. HashPassword is provided by this reference!!! Bcrypt.HashPassword generate automatic salt or provided by user according to example in site!!!!!! – Tiago NET Mar 23 '15 at 20:04
  • Access of shared member, constant member, enum member or nested type through an instance; qualifying expression will not be evaluated. Warning in this line Return Password.Equals – Tiago NET Mar 23 '15 at 21:09
  • i need use the funcion verify from bcrypt. how to retrieve the hashed password stored ???? – Tiago NET Feb 12 '16 at 22:03
  • It depends. Where did you store it? – Joel Coehoorn Feb 12 '16 at 22:06
  • i''ve use firebird! the class bcrypt have a boolean function verify. sintax is Verify(plain password, stored password). i want retrieve the stored password in my table login! – Tiago NET Feb 13 '16 at 00:23
1

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
Tiago NET
  • 153
  • 9