BCrypt generate hash and salt then compare to MySQL database - mysql

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

Related

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.

How to read a value from mysql database?

I want to be able to read a value (in this case an Group ID). All the topics and tutorials I've watched/read take the data and put it into a textbox.
I don't want to put it in a textbox in this case; I want to grab the Group ID and then say:
If Group ID = 4 then login
Here is an image of the database.
Basically, but none of the tutorials I watch or the multiple forums. None of them take a a value and say if value = 4 then login or do something else.
If text = "1" Then
MysqlConn = New MySqlConnection
MysqlConn.ConnectionString =
"server='ip of server'.; username=; password=; database="
Dim READER As MySqlDataReader
Dim member_group_id As String
Try
MysqlConn.Open()
Dim Query As String
Query = "SELECT * FROM `core_members` where name='" & TextBox2.Text & "'"
Query = "SELECT * FROM `nexus_licensekeys` where lkey_key='" & TextBox1.Text & "'"
COMMAND = New MySqlCommand(Query, MysqlConn)
READER = COMMAND.ExecuteReader
Dim count As Integer
count = 0
While READER.Read
count = count + 1
End While
Here is what I have so far. I'm kind of new implementing mysql data with visual basic and only recently started to get into it. I'm not sure what comes next or how to even start with reading the group id etc.
As I said any help from here on out would be highly appreciated of how to read the group id and say if this group id = this number then do this or that. I'm sure you get the idea.
I divided the code into UI Sub, and Data Access Function that can return data to the UI. Your Event procedure code should be rather brief and the functions should have a single purpose.
Keep your database objects local to the method. This way you can have better control. The Using...End Using blocks ensure that your database objects are closed and disposed even if there is an error.
I leave it to you to add validation code. Checking for empty TextBox or no return of records.
I hope this serves as a quick introduction to using ADO.net. The take away is:
Use Parameters
Make sure connections are closed. (Using blocks)
Private ConnString As String = "server=ip of server; username=; password=; database="
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim GroupID As String = GetGroupID(TextBox1.Text)
If GroupID = "4" Then
'your code here
End If
Dim LocalTable As DataTable = GetLicenseKeysData(TextBox1.Text)
'Get the count
Dim RowCount As Integer = LocalTable.Rows.Count
'Display the data
DataGridView1.DataSource = LocalTable
End Sub
Private Function GetGroupID(InputName As String) As String
'Are you sure member_group_id is a String? Sure looks like it should be an Integer
Dim member_group_id As String = ""
'You can pass the connection string directly to the constructor of the connection
Using MysqlConn As New MySqlConnection(ConnString)
'If you only need the value of one field Select just the field not *
'ALWAYS use parameters. See comment by #djv concerning drop table
Using cmd As New MySqlCommand("SELECT g_id FROM core_members where name= #Name")
'The parameters are interperted by the server as a value and not executable code
'so even if a malicious user entered "drop table" it would not be executed.
cmd.Parameters.Add("#Name", MySqlDbType.VarChar).Value = InputName
MysqlConn.Open()
'ExecuteScalar returns the first column of the first row of the result set
member_group_id = cmd.ExecuteScalar.ToString
End Using
End Using
Return member_group_id
End Function
Private Function GetLicenseKeysData(InputName As String) As DataTable
Dim dt As New DataTable
Using cn As New MySqlConnection(ConnString)
Using cmd As New MySqlCommand("SELECT * FROM `nexus_licensekeys` where lkey_key= #Name;", cn)
cmd.Parameters.Add("#Name", MySqlDbType.VarChar).Value = InputName
cn.Open()
dt.Load(cmd.ExecuteReader())
End Using
End Using
Return dt
End Function

BCrypt Verify stored password hash

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.

Checking if the user is already registered

I'm trying to create a register for using mysql table as if username and pass is already added it output a msgbox saying you are already registered but what happen is that it always add it even if it already exists ..
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim cn As New SqlConnection
Dim cmd As New SqlCommand
Dim cmd2 As New SqlCommand
Dim dr As SqlDataReader
cn.ConnectionString = "Server=localhost;Database=test;Uid=sa;Pwd=fadyjoseph21"
cmd.Connection = cn
cmd.CommandText = "INSERT INTO test2(Username,Password) VALUES('" & TextBox1.Text & "','" & TextBox2.Text & "')"
cmd2.CommandText = cmd.CommandText = "SELECT username, password FROM test2 WHERE username = '" & TextBox1.Text & "' and password = '" & TextBox2.Text & "'"
cn.Open()
MsgBox("Registered")
cmd.ExecuteNonQuery()
dr = cmd.ExecuteReader
If dr.HasRows Then
MsgBox("You're already registered")
End If
End Sub
End Class
You never actually check to see if the username exists.
You define a query here:
cmd2.CommandText = cmd.CommandText = "SELECT username, password FROM test2 WHERE username = '" & TextBox1.Text & "' and password = '" & TextBox2.Text & "'"
But never execute that query. Instead, you just execute the INSERT query:
dr = cmd.ExecuteReader
So the INSERT is always performed. And since an INSERT doesn't return rows, you don't see the message box.
First thing's first, fix your SQL injection vulnerability. (Personal policy, I don't like writing SQL-injectable code in an answer.) Use query parameters instead of directly concatenating user input as code:
cmd2.CommandText = "SELECT * FROM test2 WHERE username = #Username"
cmd2.Parameters.Add("#Username", SqlDbType.VarChar, 50).Value = TextBox1.Text
dr = cmd2.ExecuteReader
If dr.HasRows Then
MsgBox("You're already registered")
Return
End If
Note a couple of things here:
The use of a query parameter. I had to guess on the type and size of the column in the database, adjust that as necessary.
Only executing this one query. Don't try to execute both queries at the same time, perform the first one and then perform the second one.
You don't need, or even want, to include the password in this query. You're checking if the username already exists, that's all.
Return after showing the message, so the rest of the function doesn't execute.
Then, after that is done, you can perform the INSERT operation:
cmd.CommandText = "INSERT INTO test2(Username,Password) VALUES(#Username,#Password)"
cmd.Parameters.Add("#Username", SqlDbType.VarChar, 50).Value = TextBox1.Text
cmd.Parameters.Add("#Password", SqlDbType.VarChar, 50).Value = TextBox2.Text
cmd.ExecuteNonQuery()
This will perform the INSERT operation. So if the Return above was never encountered then the username is unique and can be inserted.
Also: You should not be storing user passwords in plain text. This is grossly irresponsible to your users and exposes their private data to attackers. Instead, obscure the password with a one-way hash so that it can't be read in its original form.
A couple other things:
Use meaningful variable names. The whole reason you were having this problem was because you were getting confused between cmd and cmd2. If your variable names carry semantic meaning, your code is a lot easier to read and understand.
Make use of the Using block when you have disposable resources, such as a database connection. In general you want to open, use, and close a database connection in as small a scope as possible. Leaving open connections hanging around is a Bad Thing.
Add unique key constraint on username and hence when it already exist it will throw duplicate entry exception.
Secondly, never save plain text password in Database, it must be hasheh and encrypted. I would suggest to better use Bcrypt of atleast level 10 to generate hashed password and also use Bcrypt dynamic salt which is mostly preferred now.
Thirdly, always use parameterized query to avoid your program from mysql injection.
For example:-
Normal:
SELECT * FROM customers WHERE username = 'timmy'
Injection:
SELECT * FROM customers WHERE username = '' OR 1''

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?)