BCrypt Verify stored password hash - mysql

Storing the hashed password in database is successful. But when verifying if the input password and the hash stored in database, it always returning false.
Dim pw As String = txt_password.Text
Dim salt As String = BCrypt.Net.BCrypt.GenerateSalt(12)
Dim hash As String = BCrypt.Net.BCrypt.HashPassword(pw, salt)
With sqlLogin
.Parameters.AddWithValue("#userid", txt_username.Text)
.Parameters.AddWithValue("#userpass", hash)
End With
Dim Reader As MySqlDataReader = sqlLogin.ExecuteReader()
If (BCrypt.Net.BCrypt.Verify(pw, hash)) Then
MessageBox.Show("Login Succesful!", "Success!", MessageBoxButtons.OK, MessageBoxIcon.Information)
Me.Hide()
Reader.Close()
ElseIf Reader.HasRows = False Then
MessageBox.Show("Invalid Login Information!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
txt_username.Text = ""
txt_password.Text = ""
txt_username.Focus())
End If

Especially when it comes to encryption, you ought to have some general idea of the principles and concepts involved. Salted Password Hashing explains common pitfalls and makes a number of recommendations (one is BCrypt, so you may be on the right path).
It looks like you are not reading the stored hash from the DB before you verify. You don't show how it is saved, but that is important in order for it to verify.
' cuts down on dot operators
Imports BCryptor = BCrypt.Net.BCrypt
Create New Logon
' new user save
Dim sql = "INSERT INTO userlogin (email, username, pwhash) VALUES (#email, #n, #pw)"
' prep:
Dim salt = BCryptor.GenerateSalt(12) ' == 2^12
Dim hash = BCryptor.HashPassword(tbPass)
' to do: Try/Catch for an email that already exists
Using dbCon As New MySqlConnection(MySQLConnStr),
cmd As New MySqlCommand(sql, dbCon)
cmd.Parameters.Add("#email", MySqlDbType.Text).Value = tbEmail
cmd.Parameters.Add("#n", MySqlDbType.Text).Value = tbUserName
cmd.Parameters.Add("#pw", MySqlDbType.Text).Value = hash
dbCon.Open()
cmd.ExecuteNonQuery()
End Using
Verify an Attempt
Dim bRet As Boolean = False
' login user
Dim sql = "SELECT pwhash FROM userlogin WHERE email = #email"
Using dbCon As New MySqlConnection(MySQLConnStr),
cmd As New MySqlCommand(sql, dbCon)
' data for the where clause
cmd.Parameters.Add("#email", MySqlDbType.Text).Value = tbEmail
dbCon.Open()
Using rdr = cmd.ExecuteReader()
' read from the reader to load data
If rdr.Read() Then
' get the saved hash
Dim savedHash = rdr.GetString(0)
bRet = BCryptor.Verify(tbPass, savedHash)
Else
bRet = False
End If
End Using
' return whether the hash verified
Return ret
End Using
Notes
DbConnection, DbCommand and DbDataReader all implement Dispose which means they may very well allocate resources which need to be released. The code uses each of them in a Using block. This creates them at the start and disposes of them at the end of the Block.
This uses an email for the unique identifier because there are a lot of Steves out there. This means the SQL will return one record at most.
After loading the hashed pw from the DB, use it to verify the password attempt entered. Your code seems to be creating a new hash (and doesnt actually load anything from the DB).
The random salt originally generated when the account was created becomes part of the hash (as well as the work factor you used) as shown here:
Dim pw = "My!Weak#Pa$$word"
Dim salt = BCryptor.GenerateSalt(12)
Dim hash = BCryptor.HashPassword(pw, salt)
Console.WriteLine(salt)
Console.WriteLine(hash)
Output:
$2a$12$XPC20niJIhZPxaKvJkSUfO
$2a$12$XPC20niJIhZPxaKvJkSUfO/rwIetoScCze.tOcVS/aJzowvjpCPlq
The 12 after "$2a$" is the work factor.

Related

BCrypt generate hash and salt then compare to MySQL database

I'm looking to BCrypt to hash and salt and compare it to the hash stored in my database. There seems to be a lack of vb.net with BCrypt, which is why I'm asking.
So, from what I understand is we generate a hash and a salt when the user enters their password. Then we compare that hash to the one in the database, however since I've found nothing online for vb.net I'm not sure how to do it.
This is what I have so far. If the code looks messy it's because I copied and pasted it from Visual Studio to here, on VS it looks neat and tidy.
Now I know there will be flaws with the code etc. I'm not that bothered as this is for personal use and learning. Just need to learn how to generate salt + hash with BCrypt and then compare it with the already hash & salted password in my database but in terms of how to go about and redo this bit of code and implement the check that both salt & hashes are the same I'm stuck.
Dim pw As String = TextBox_Password.Text
Dim Salt As String = BCrypt.Net.BCrypt.GenerateSalt(12)
Dim Hash As String = BCryot.Net.BCrypt.HashPassword(pw, salt)
Try
Connection.Open()
Dim SQLQuery
SQLQuery = "SELECT * FROM `core_members` where name='" & TextBox_Username & " ' and members_pass_hash='" & I don't know if you're meant to put Hash? here to hash the inputed password from the user? Or the TextBox_Password.Text & "'"
'As for the verify function... to compare the hashed password I do try to do this
If (BCRYpt.Net.BCrypt.Verify(pw, hash)) Then
Command1 = New MySqlCommand(SQLQuery, Connection)
READER = Command1.ExecuteReader
Dim Count As Integer
count = 0
While READER.read
count += 1
end while
READER.close()
If count = 1 then
'User Successfully Logged In
end if
'I definitely know the count = 1 etc probably not the best way to allow a user to login. I've seen something with MyData.HasRows or something like that to login?
'I know that the code above is probably no where near close to actually how it's done but as I said due to the lack of documentation with vb.net and BCrypt not making it easy.
Any help with cleaning up the login function would be great including comparing the hashed password in my database.
Just want to give the biggest thanks to #Mary for being so kind and helpful for providing a solution to solve my biggest problem thus far! Thank you!
I'm only making this Answer because for me there were a few typo's I had to fix, but all of it goes to Mary!
So, first off I'm using a mysql.dat.dll which uses Imports MySql.Data.MySqlClient (Which changes a few things from Mary's code)
Imports Crypt = BCRypt.Net.BCrypt - Allows us to use Crypt Instead of having to type the whole BCrypt.Net.BCrypt
Please note for me, if your textbox's are empty then you will get an error Object Reference not set to an instance of an object. This is if you're on visual studio. It won't happen if you run the program as a normal user. I will tell the user make sure they have entered a username and password
Imports Crypt = BCRypt.Net.BCrypt
Private Sub VerifyPassword()
try
Dim Password As String = "TextBox_UserPassword"
Dim Hashword As String = ""
Using Conn As New MySqlCommand(Connection),
Command As New MySqlCommand("SELECT password FROM members where Username= #Username;", Conn)
Command.Parameters.Add("#Username", MySqlDbType.VarChar).Value = TextBox_Username
Conn.Open()
Hashword = Command.ExecuteScalar.ToString
End Using
Dim Result = Crypt.Verify(Password, Hashword)
If result = true then
MsgBox("Logged in")
else
MsgBox("Logged in Failed")
end if
Catch ex As Exception
MessageBox.Show(ex.Message) 'Optional'
MsgBox("Make sure have entered a Username or Password", vbcritical) 'If the textbox have nothing it will remind the user to make sure they enter a username or password'
End try
End Sub
I used Sql Server to test code because it was what I had handy. It will work the same for MySql. With BCrypt you don't have to store the salt separately.
Private Sub InsertNewUser()
Dim HashWord As String = BCrypt.Net.BCrypt.HashPassword(TextBox2.Text, BCrypt.Net.BCrypt.GenerateSalt(12))
Using cn As New SqlConnection(My.Settings.PublishCon),
cmd As New SqlCommand("Insert Into Users (UserName, Password) Values (#Name, #HashWord);", cn)
cmd.Parameters.Add("#Name", SqlDbType.VarChar, 100).Value = TextBox1.Text
cmd.Parameters.Add("#HashWord", SqlDbType.VarChar, 100).Value = HashWord
cn.Open()
cmd.ExecuteNonQuery()
End Using
End Sub
Private Sub VerifyPassword()
Dim pw As String = TextBox2.Text
Dim HashWord As String = ""
Using cn As New SqlConnection(My.Settings.PublishCon),
cmd As New SqlCommand("SELECT Password FROM Users where UserName= #UserName;", cn)
cmd.Parameters.Add("#UserName", SqlDbType.VarChar).Value = TextBox1.Text
cn.Open()
HashWord = cmd.ExecuteScalar.ToString
End Using
Dim result = BCrypt.Net.BCrypt.Verify(pw, HashWord)
If result Then
MessageBox.Show("Successful Login")
Else
MessageBox.Show("Sorry login failed")
End If
End Sub

VB.net and Mysql Login Problem Using Two Tables

How can i login using doctor level in his table.Because everytime i log the receptionist form is always popping even when i log using the user and pass of the doctor maybe my syntax is wrong please help me
PS: Doctors Forms and Receptionists Forms are different.
This is my code:
Try
mycon.Open()
Dim query1 As String
Dim query2 As String
Dim name As String
query1 = "SELECT * FROM receptionisttbl"
command = New MySqlCommand(query1, mycon)
reader = command.ExecuteReader
With reader
.Read()
If .Item("level") = "1" Then
With reader
.Read()
name = .Item("name")
End With
MsgBox("Welcome " + name)
Form1.Show()
command.Dispose()
reader.Close()
mycon.Close()
Else
mycon.Open()
query2 = "SELECT * FROM doctortbl"
command = New MySqlCommand(query2, mycon)
reader = command.ExecuteReader
With reader
.Read()
If .Item("level") = "2" Then
With reader
.Read()
name = .Item("name")
End With
MsgBox("Welcome " + name)
Form2.Show()
command.Dispose()
reader.Close()
mycon.Close()
End If
End With
End If
End With
Catch ex As Exception
MsgBox(ex.ToString)
Finally
mycon.Dispose()
End Try
First, let's consider your requirements.
Provide a login form to validate users.
If the user is valid (userName and matching password are found in the database) then display a Form based on the level of the user.
Considering the requirements look at the design of the table in the database. Let's call it MDUsers.
Now to the columns.
UserName as an VarChar size 100 and Primary key of the table
UserPassword as VarChar size 100
Level an Integer
Now you have fields for all the data you need for your requirements.
Add some sample data to your table.
Next think about what you need from the user to accomplish your requirements. Design your user interface accordingly.
Your login form will have 2 text boxes (userName and password) and a button (Login).
Then you need to think about the code for the Login button.
You need to connect to your database and check if there is a match to userName and password. Once you find a match you only need to retrieve a single piece of data to complete your requirements, the level.
Now how can we ask the database to do all that?
Select level From MDUsers Where userName = #userName And UserPassword = #pword;
Since userName is the Primary Key of the table we know we will find no more than one record with that userName. If the password matches too then we have a valid user and we will return the single piece of data, level for that record.
Using...End Using blocks ensure that your database objects are closed and disposed even if there is an error. You can pass your connection string directly to the constructor of the connection and pass the query string and connection to the constructor of the command.
Always use parameters.
Since we only need a single piece of data we can use .ExecuteScalar
Private Sub OpCode()
Dim retVal As Object
Using cn As New MySqlConnection("Your connection string"),
cmd As New MySqlCommand("Select level From MDUsers Where userName = #userName And UserPassword = #pword;", cn)
cmd.Parameters.Add("#userName", MySqlDbType.VarChar, 100).Value = txtName.Text
cmd.Parameters.Add("#pword", MySqlDbType.VarChar, 100).Value = txtPassword.Text
cn.Open()
retVal = cmd.ExecuteScalar
End Using
If retVal Is Nothing Then
MessageBox.Show("Login failed")
Return
End If
If CInt(retVal) = 1 Then
Form1.Show()
ElseIf CInt(retVal) = 2 Then
Form2.Show()
Else
MessageBox.Show("Level not recognized")
End If
End Sub
You will have to check MySql rules for column names and table names being aware of reserved words.

Parameterize SQL Queries

I want parameterize some SQL Statements so my code isn't vunerable to SQL Injections any longer But i have actually no plan how to parameterize for example a where clause.
Dim accID As String = DatabaseConnecter.readField("SELECT ID FROM accounts WHERE accountname ='" & user & "' AND password='" & pw & "';")
The Problem is if you type in a given username, for example test and extend the username with. You can log in without entering the password into the Application.
Edit:
Public Function readField(ByVal sql As String) As String
Dim output As String = "ERROR"
Using cn = New MySqlConnection(connString.ToString())
Using cmd = New MySqlCommand(sql, cn)
cn.Open()
Using rd = cmd.ExecuteReader()
Try
rd.Read()
output = rd.GetString(0)
rd.Close()
Catch ex As Exception
End Try
End Using
cn.Close()
End Using
End Using
Return output
End Function
ยดยดยด
To have a parameterized query you need to create parameters and write a proper SQL text where, in place of values typed directly from your user, you have parameter placeholders.
So, for example, you sql text should be something like this
Dim sqlText = "SELECT ID FROM accounts WHERE accountname =#name AND password=#pwd"
Now you have a parameterized text, but stil we need to create the parameters that will be sent to the database engine together with your sql command.
You can create the parameter (two in this case) in this way before calling the method that executes the query
Dim p1 as MySqlParameter = new MySqlParameter("#name", MySqlDbType.VarChar)
p1.Value = user
Dim p2 as MySqlParameter = new MySqlParameter("#pwd", MySqlDbType.VarChar)
p2.Value = password
Dim pms As List(Of MySqlParameter) = new List(Of MySqlParameter)()
pms.Add(p1)
pms.Add(p2)
Now we need to pass this list to your method (and this requires changes to your method signature)
DatabaseConnecter.readField(sqlText, pms)
The method itself should change to something like
Public Function readField(ByVal sql As String, Optional pms As List(Of MySqlParameter) = Nothing) As String
Dim output As String = "ERROR"
Using cn = New MySqlConnection(connString.ToString())
Using cmd = New MySqlCommand(sql, cn)
cn.Open()
' This block adds the parameter defined by the caller to the command
' The parameters are optional so we need to check if we have really received the list or not
if pms IsNot Nothing Then
cmd.Parameters.AddRange(pms.ToArray())
End If
Using rd = cmd.ExecuteReader()
Try
rd.Read()
output = rd.GetString(0)
rd.Close()
Catch ex As Exception
End Try
End Using
' no need to close when inside a using block
' cn.Close()
End Using
End Using
Return output
End Function
The method now has an optional parameter that will contain the list of the parameters required by the query (or nothing if your query doesn't require parameters). This list is added to the command parameters collection and the query is now executed.
Final Note: Storing passwords in clear text into a database is a well known security problem. I suggest you to search about how to store passwords in a database.
Private Function GetID(User As String, pw As String) As String
Using cmd As New SqlCommand("SELECT ID FROM accounts WHERE accountname =#user AND password=#password", New SqlConnection(SQLConnString))
cmd.Parameters.AddWithValue("#user", User)
cmd.Parameters.Add("#password", SqlDbType.NVarChar)
cmd.Parameters("#password").Value = pw
Try
cmd.Connection.Open()
Return cmd.ExecuteScalar()
Catch ex As Exception
'handle error
Return Nothing
Finally
cmd.Connection.Close()
End Try
End Using
End Function
I've demostrated two methods of setting the parameters. Search for more info or comparison.

MySQL inserting a column into a variable

I'm working on a project that needs to prevent double username inserted in a database my code looks like this:
Command = New MySqlCommand("Select * From userscanner", Connection)
Command.ExecuteNonQuery()
Dim dr As MySqlDataReader
dr = Command.ExecuteReader
With dr
.Read()
Dim check As String = .Item(1)
.Close()
If check = txtbox_username.Text Then
MsgBox("Username Already Taken")
Exit Sub
My problem is it only gets 1 column or is there any other way to prevent double username in my database?
I need all column in my username column. I'm using VB and MySQL.
You should ask your database if a particular user name exists or not.
This could be done with a WHERE clause
Dim sqlText = "SELECT 1 FROM userscanner WHERE username = #name"
Using Command = New MySqlCommand(sqlText, Connection)
Command.Parameters.Add("#name", MySqlDbType.VarChar).Value = txtbox_username.Text
Dim result = Command.ExecuteScalar()
if result IsNot Nothing Then
MsgBox("Username Already Taken")
End If
End Using
Here I assume that your database table named userscanner has a field named username (the field retrieved by your code with Item(1)) where you store the user names. Adding a WHERE condition and a simple return of 1 if there is a record allows to use the simple ExecuteScalar that returns the value 1 if there is a matching record to your textbox value or Nothing if there is no record
I found an answer to the question on my own.
Command = New MySqlCommand("Select * From userscanner WHERE Username = '" & txtbox_username.Text & "'", Connection)
Command.ExecuteNonQuery()
Dim dr As MySqlDataReader
dr = Command.ExecuteReader
dr.Read()
dr.Close()
If dr.HasRows Then
MsgBox("Username Already Taken")
Thanks for the help guys

String from Database set as public string

Ok from the answer from the previous question the reasoning still applies here but this time A different issue. There is a login system (Loginvb.vb) that I got for the launcher I was creating and was wondering 2 things:
Is there a better way to do the Login check with the database (as in
more secure) (the login style will have a web based registration
setting via PHP script)?
Is there a way to take a certain column (labled as access) in the database and put it
as a public string so I can check if it will equal 1 2 or 3 in a
different form labeled as Main.vb
Here is the current login check:
Public Sub login_Click(sender As Object, e As EventArgs) Handles login.Click
If txtuserName.Text = "" Or txtpassWord.Text = "" Then
MsgBox("You cannot progress until you login....(moron =p)")
Else
'Connects To the Database
Dim connect As MySqlConnection
connect = New MySqlConnection()
connect.ConnectionString = "server=127.0.0.1;user id=sc;Password=derp;database=sclaunch" 'not the actual login ;)
Try
connect.Open()
Catch myerror As MySqlException
MsgBox("Error Connecting to Database. Please Try again !")
End Try
'SQL Query To Get The Details
Dim myAdapter As New MySqlDataAdapter
Dim sqlquerry = "Select * From login where username = '" + txtuserName.Text + "' And password= '" + txtpassWord.Text + "'"
Dim myCommand As New MySqlCommand()
'My fail attempt at what I am trying to do :(
Dim sql22 As MySqlConnection
sql22 = New MySqlConnection()
sql22.ConnectionString = "Select * From login where access ="
'End of fail attempt
myCommand.Connection = connect
myCommand.CommandText = sqlquerry
'Starting The Query
myAdapter.SelectCommand = myCommand
Dim mydata As MySqlDataReader
mydata = myCommand.ExecuteReader
'To check the Username and password and to validate the login
If mydata.HasRows = 0 Then
MsgBox("Invalid Login")
Else
'fail testing xD
Label3.Text = sql22
MsgBox("You are now Loged In!")
End If
End If
End Sub
Still basically learning more and more as I am coding all this got to love trial and error and the moments where you get stuck =/ (Sorry to the admins or whatever for fixing tag issues still new to the site xD)
Assuming that the same table login that contains the credentials contains also the access column that you want to retrieve, then I have changed a lot of your code
Dim sqlquerry = "Select * From login where username = #name AND password=#pwd"
Dim myCommand As New MySqlCommand(sqlquery, connect)
myCommand.Parameters.AddWithValue("#name", txtuserName.Text)
myCommand.Parameters.AddWithValue("#pwd", txtpassWord.Text)
Dim mydata = myCommand.ExecuteReader
If mydata.HasRows = False Then
MsgBox("Invalid Login")
Else
' the same record that contains the credentials contains the access field'
mydata.Read()
Label3.Text = mydata("access").ToString()
MsgBox("You are now Loged In!")
End If
What I have changed:
Removed the string concatenation and added the appropriate parameters
Removed myAdapter and every references to it (not needed, you don't
fill DataTable/DataSet)
Removed sql22 and every references to it. It's a Connection and you
try to use like a Command
Fixed the check on HasRows (Returns a boolean not an integer. Are you
using Option Strict Off?)