How to salt and hash a password - vb.net

The code below allows a user to enter user name and password to log in to enter marks of students. SQL data reader verifies the user credentials from the database before authentication takes place. I would be grateful if someone could modify the code by salting and hashing the password.
Dim frm As New MarksEntryFrm
Dim flag As Boolean
flag = False
If cboForm.Text = "" Or cboAcadYear.Text = "" Or cboSubjCode.Text = "" Or txtUserName.Text = "" Or txtPassword.Text = "" Then
MessageBox.Show("Please any of the fields cannot be left blank", "Blank fields", MessageBoxButtons.OK, MessageBoxIcon.Error)
Else
cmd = New SqlCommand("Select a.Form,a.AcademicYear,b.SubjectID,b.UserID,b.Password,c.Term from StudentDetails.Programmes a, StudentDetails.Subjects b,RegistrationDetails.Registration c where b.SubjectID='" & cboSubjCode.SelectedItem & "' and b.UserID='" & txtUserName.Text & "' and b.Password='" & txtPassword.Text & "' collate Latin1_General_CS_AS", cn)
cmd.Parameters.AddWithValue("#UserID", txtUserName.Text) 'protects the database from SQL Injection
cmd.Parameters.AddWithValue("#Password", txtPassword.Text) 'protects the database from SQL Injection
dr1 = cmd.ExecuteReader
ctr = ctr + 1
If dr1.Read Then
frm.Show()
ctr = 0
Hide()
ElseIf ctr < 3 Then
MessageBox.Show("Incorrect Subject Code,User Name or Password. Please try again.", "Wrong data entered", MessageBoxButtons.OK, MessageBoxIcon.Asterisk)
Else
MsgBox("Unathorized access. Aborting...")
Close()
End If
dr1.Close()
End If
End Sub

P.S. Akaglo, a better way to check if any fields were left empty is to use the String.IsNullOrEmpty() method. Your method will not detect any null or space characters.

Use a parametrized query
Dim cmdText As String = _
"INSERT INTO Customer(UserName, [Password]) VALUES (#UserName,#Password)"
Dim cmd As SqlCommand = New SqlCommand(cmdText, con)
With cmd.Parameters
.Add(New SqlParameter("#UserName", txtUserName.Text))
.Add(New SqlParameter("#Password", txtPassword.Text))
End With

In the .NET membership providers you will get hashing and seeding given by the .NET library which should be implemented correctly. This IMHO is much to prefer for rolling your own solution. There is an introduction to membership here
IF you prefer to make your implementation the seeding and hashing part is not overtly complex. The seeding could be as simple as adding a random string to the original password prior to hashing it. You then store the hash and the seed in the database. When the user provides the password you then simply readd the seed and compare the hashes. Note that when you make random strings for cryptographic purposes you should not rely on Random, but rather go for some cryptographically secure random generator. The System.Security.Cryptography also contains implementations of many suitable hashing algorithms (sha1, sha256 or similar).
Again: In my opinion you should go for a solution using the SqlMembershipProvider to avoid reimplementing security critical stuff.

Related

How to resolve the syntax error in UPDATE statement

What is wrong with this code? I did everything but I still get a
syntax error in UPDATE statement
Dim konfirmasi As String = MsgBox("Yakin data ingin diubah ?", vbQuestion + vbYesNo, "Konfirmasi")
If konfirmasi = vbYes Then
SqlQuery = "Update Tabel_Pengguna set " & _
"Username = '" & txtUsername.Text & "'," & _
"Password ='" & txtPassword.Text & "' where Kode_Pengguna = '" & txtKodePengguna.Text & "'"
CMD = New OleDbCommand(SqlQuery, DB)
CMD.ExecuteNonQuery()
MsgBox("Data berhasil diubah", vbInformation, "Informasi")
To make MsgBox work you would need to use the bitwise Or operator. This function returns a MsgBoxResult not a String. I suggest you change to the .net MessageBox and leave the old VB6 code behind.
Private Sub OPCode()
'Dim konfirmasi As MsgBoxResult = MsgBox("Yakin data ingin diubah ?", vbQuestion Or vbYesNo, "Konfirmasi")
Dim konfirmasi As DialogResult = MessageBox.Show("Yakin data ingin diubah ?", "Konfirmasi", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
If konfirmasi = DialogResult.Yes Then
UpdatePengguna(txtUsername.Text, txtPassword.Text, txtKodePengguna.Text)
End If
End Sub
Keep connection local to the method where they are used so they can be closed and disposed with Using...End Using blocks. In this code both the connection and the command are included in the Using block; note the comma at the end of the first line of the Using.
Always use parameters to avoid Sql injection. With OleDb the names of the parameters are ignored but we use descriptive names to make reading the code easier. It is the order that matters. The order that the parameters appear in the Sql statement must match the order which the parameters are added to the parameters collection. You will have to check your database for the correct datatypes and field sizes. I suspect Kode_Pengguna might be a numeric type. If so, be sure the change the datatype of the passed in parameter PenKode.
I believe you are neglecting to open your connection unless your are passing around open connections (be still my heart!). Open the connection at the last minute, directly before the .Execute... and close it as soon as possible with the End Using.
Private Sub UpdatePengguna(UserName As String, Password As String, PenKode As String)
Using cn As New OleDbConnection(ConStr),
cmd As New OleDbCommand("Update Tabel_Pengguna Set [UserName] = #Username, [Password] = #Password Where Kode_Pengguna = #Kode;", cn)
cmd.Parameters.Add("#Username", OleDbType.VarChar, 100).Value = UserName
cmd.Parameters.Add("#Password", OleDbType.VarChar, 100).Value = Password
cmd.Parameters.Add("#Kode", OleDbType.VarChar, 100).Value = PenKode
cn.Open()
cmd.ExecuteNonQuery()
End Using
MessageBox.Show("Data berhasil diubah", "Informasi", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
I really hope you are not saving passwords as plain text.

vb.net // Failed to connect to the database ? Login System Check the user if admin or not

Hello guys, I am implementing a login system where there are two users it's either the admin or the superadmin, however it always fail to connect to the database. I'm kinda new to VB.net and I'm trying to figure out on how this make thing work and yep I searched up the web on how to create on but it fails, and btw here's the error log generated after logging in
Failed to Connect to the Database
A first chance exception of type 'System.InvalidOperationException' occurred in System.Data.dll
Imports System.Data.OleDb
Imports System.Data
Public Class LoginFrm
Private Sub LoginBtn_Click_1(sender As Object, e As EventArgs) Handles LoginBtn.Click
If userBox.Text = "" Or passwordBox.Text = "" Then
MessageBox.Show("Username and password are blank", "Authentication Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Else
Dim conn As New System.Data.OleDb.OleDbConnection()
conn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\ResortReservationSystem.accdb"
Dim sql As String = "SELECT * FROM userTable WHERE userName='" & userBox.Text & "' AND passWord = '" & passwordBox.Text & "'"
Dim sqlCom As New System.Data.OleDb.OleDbCommand(sql)
sqlCom.Connection = conn
sqlCom.Connection.Open()
Dim sqlRead As System.Data.OleDb.OleDbDataReader = sqlCom.ExecuteReader()
If sqlRead.Item("userType") = "SuperAdmin" Then
welcomeFrm.Show()
Me.Hide()
End If
If sqlRead.Item("userType") = "Admin" Then
manageEmployeeForm.Show()
Else
MessageBox.Show("Username and Password do not match.", "Authentication Failure", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
userBox.Text = ""
passwordBox.Text = ""
userBox.Focus()
End If
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Me.Close()
End Sub
End Class
Edit: I have fixed some issues and now I am encountering this,
No data exists for the row/column. For what I know, .Item is to get the fetch the data, but it seems like it doesnt work for me.
table name: userTable
fields: userName, passWord, userType
datas: John, Doe, SuperAdmin
The point of a data reader is to read data. If you have done any reading on data readers and their use then you know that you have to call the Read method to actually read a record. You aren't calling Read, hence there's no data in your data reader.
If userBox.Text = "" Or passwordBox.Text = "" Then
Change the Or to OrElse to short circuit the If.
Connections and commands need to be closed and disposed. A Using...End Using block will do this for you even if there is an error.
You can pass the connection string directly to the constructor of the connection. Likewise, pass the command text and connection directly to the constructor of the command.
Never concatenate strings with user input to build sql statements. You risk sql injection which can ruin your database. It also makes the sql statement easier to write because you don't have to use all those single and double quote and ampersands.
You are only using a single piece of data so don't return all the fields. You only need userType.
Always use parameters. Access (OleDb) does not care about the name of the parameter. I just use appropriate names for readability. What is important is the order that the parameter appears in the sql statement must match the order that the parameter is added to the parameters collection. I had to guess at the datatype and field size of the parameters. Check your database for the real values and correct the code accordingly.
Since we are only getting a single piece of data, we can use .ExecuteScalar which returns the first column of the first row of the result set.
The End Using closes and disposes the connection and command so now we can mess with returned data.
Sidenote: Your problem was that a reader does not start reading the returned rows until you call reader.Read. This is no longer relevant since we are not using a reader.
Private Sub LoginBtn_Click_1(sender As Object, e As EventArgs) Handles LoginBtn.Click
Dim AdminType As String
If userBox.Text = "" OrElse passwordBox.Text = "" Then
MessageBox.Show("Username and password must be filled in.", "Authentication Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Else
Using conn As New System.Data.OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\ResortReservationSystem.accdb"),
sqlCom As New OleDbCommand("SELECT userType FROM userTable WHERE userName= #User AND passWord = #Password", conn)
With sqlCom.Parameters
.Add("#User", OleDbType.VarChar, 100).Value = userBox.Text
.Add("#Password", OleDbType.VarChar, 100).Value = passwordBox.Text
End With
conn.Open()
AdminType = sqlCom.ExecuteScalar.ToString
End Using
If String.IsNullOrEmpty(AdminType) Then
MessageBox.Show("Username and Password do not match.", "Authentication Failure", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
userBox.Text = ""
passwordBox.Text = ""
userBox.Focus()
ElseIf AdminType = "SuperAdmin" Then
welcomeFrm.Show()
Me.Hide()
ElseIf AdminType = "Admin" Then
manageEmployeeForm.Show()
End If
End If
End Sub

Failed to read when no data is present

i have this code,,its work (kind of).
Dim connString As String = ConfigurationManager.ConnectionStrings("connectionstring").ConnectionString
Dim conn As New SqlConnection(connString)
conn.Open()
Dim comm As New SqlCommand("SELECT username, Password,type FROM users WHERE username='" & TextBox1.Text & "' AND Password='" & TextBox2.Text & "'", conn)
Dim reader As SqlDataReader
reader = comm.ExecuteReader
Dim count As Integer
count = 0
While reader.Read
count = count + 1
End While
If count = 1 Then
MessageBox.Show("username and password are correct")
Form2.Show()
Form2.Label1.Text = Me.TextBox1.Text
Form2.Label2.Text = reader(2).ToString
ElseIf count > 1 Then
MessageBox.Show("username and password are duplicated")
Else
MessageBox.Show("username and password are wrong")
End If
im getting error with this line:
Form2.Label2.Text = reader(2).ToString
and error is "Invalid attempt to read when no data is present"
why its says "no data"
i have all data in database?
can someone help me to correct this code?
thank you ..
You should not be using a loop at all. There should be no way that you can get more than one record so what use would a loop be? You should be using an If statement and that's all:
If reader.Read() Then
'There was a match and you can get the data from reader here.
Else
'There was no match.
End If
If it's possible to have two records with the same username then there's something wrong with your database design and your app. That column should be unique and your app should be testing for an existing record when someone tries to register.
A SqlDataReader is a forward only data read element. The error is occurring because you're calling the reader's READ function twice; once as true to increment to 1, and a second time to get a false to fall out of the while statement. Since you're no longer in the WHILE statement, the reader had to have read the end of the result set, thus there is no data for you to read.
Consider the changed code below:
Dim connString As String = ConfigurationManager.ConnectionStrings("connectionstring").ConnectionString
Dim count As Integer = 0
Dim userType as string = ""
Using conn As New SqlConnection(connString)
conn.Open()
Using Comm as SqlCommand = conn.CreateCommand
comm.commandText = "SELECT username, Password, type FROM Users WHERE username = #UserName AND Password = #Pwd; "
comm.parameters.AddWithValue("#Username", TextBox1.Text)
comm.parameters.AddWithValue("#Password", Textbox2.text)
Dim reader As SqlDataReader
reader = comm.ExecuteReader
If reader IsNot Nothing Then
If reader.HasRows() Then
While reader.read
count = count + 1
If Not reader.IsDbNull(2) Then userType = reader(2).ToString
End While
End If
If Not reader.IsClosed Then reader.close
reader = Nothing
End If
End Using
End Using
If count = 1 Then
MessageBox.Show("username and password are correct")
Form2.Show()
Form2.Label1.Text = Me.TextBox1.Text
Form2.Label2.Text = userType
ElseIf count > 1 Then
MessageBox.Show("username and password are duplicated")
Else
MessageBox.Show("username and password are wrong")
End If
First off, SQLParameters are your friend. Learn them. They are the single easiest way to fight against SQL Injection when using the SqlClient classes.
Secondly, notice that I'm doing the actual retrieval of the data from the reader inside the WHILE loop. This ensures that there's actual data for me to read.
Third, notice the USING statements on the SqlConnection and SqlCommand objects. This helps with garbage collection, and has a couple of other benefits as well.
Finally, notice the checks I'm doing on the SqlDataReader before I ever attempt to access it. Things like that would prevent from another error appearing if you did not return any results.

How do I get a login form to reject entries with wrong capitalization?

I have the following code so far for a login form taking data from a database:
Dim myconnection As New SqlConnection("server=classified;database=classified")
myconnection.Open()
Dim theQuery As String = " SELECT Username, Password FROM Accounts WHERE (Username = '" & TextBox1.Text & "' ) AND (Password = '" & TextBox2.Text & "')"
Dim repeatChecker As SqlCommand = New SqlCommand(theQuery, myconnection)
'mycommand.ExecuteNonQuery()
Using reader As SqlDataReader = repeatChecker.ExecuteReader()
If reader.HasRows Then
' User already exists
While reader.Read()
If reader("Password") = TextBox2.Text.ToString And reader("Username").ToString = TextBox1.Text Then
MessageBox.Show("Logged in successfully as " & TextBox1.Text, "", MessageBoxButtons.OK, MessageBoxIcon.Information)
Firs.Show()
Me.Close()
'Clear all fields
End If
End While
Else
MessageBox.Show("Invalid username or password.", MsgBoxStyle.Critical)
End If
End Using
myconnection.Close()
If I put in the correct login info but with wrong capitalization, I don't get an acceptance or a rejection, the program just sits there and does nothing. How can I get a denial of a login when the capitalization is wrong?
As written, you really can't discern just a case-mismatch from a query as you've illustrated in this code. If a database is set up for case-sensitivity, a query will fail if two strings don't match even for the difference of a single mismatched character, but it doesn't retain that as a reason for the mismatch anymore than it would for, say "Apple" not matching "Banana."
Please note that, as the commentators of your question stated:
You're vulnerable to SQL-Injection attacks.
You should never store passwords in clear text in your DataBase. Once the DB gets cracked, all credentials are compromised. Not to mention evil DB-admins that might get tempted to misuse those credentials...
Case-Sensitivity in a password is a good thing.
With those things mentioned, if you want to provide your users with the comfort of a not case-sensitive username, just cast the TextBox1.Text as well as the query result for the Username to upper case by changing (Username = '" & TextBox1.Text & "' ) to (UPPER(Username) = '" & TextBox1.Text.ToUpper() & "')

Login form within vb.net- to direct to different forms

In a Login form for VB.Net connected to an Oracle database.. Is there a way of inserting an If statement to direct different users to different forms.. Eg, an accountant to the accounting home page or a driver to a driver homepage even though all there ID's and passwords are in the one table within the database.
There is a POSITION field within the database and this is what I would like to use to differentiate the different users levels of access.
Here is the code working so far:
Dim conn As New OleDb.OleDbConnection
conn.ConnectionString = _
"Provider=msdaora;Data Source=orabis;User Id=112221800;Password=112221800;"
conn.Open()
Dim parmuser As New OleDb.OleDbParameter
parmuser.OleDbType = OleDb.OleDbType.Char
parmuser.Value = txtStaffNo.Text
Dim parmpass As New OleDb.OleDbParameter
parmpass.OleDbType = OleDb.OleDbType.Char
parmpass.Value = txtPassword.Text
Dim cmd As New OleDbCommand
cmd.Connection = conn
cmd = New OleDbCommand("select STAFFID,PASSWORD from STAFF where STAFFID ='" & txtStaffNo.Text & "' and PASSWORD ='" & txtPassword.Text & "'", conn)
cmd.CommandType = CommandType.Text
Dim dr As OleDb.OleDbDataReader
dr = cmd.ExecuteReader()
If txtStaffNo.Text = "" Or txtPassword.Text = "" Then
MessageBox.Show("You have not entered any values!", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error)
ElseIf dr.Read() Then
txtStaffNo.Text = dr("STAFFID")
txtPassword.Text = dr("PASSWORD")
MsgBox("Access Allowed")
CustOption.Show()
Me.Hide()
Else
'MessageBox.Show("Wrong Username and Password", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
'intCount = intCount + 1
End If
For a web site/app use
switch (position){
case "Admin":
Server.Transfer("AdminHomePage.aspx";
brea;
case "blabla":
//and so on
default:
Server.Transfer("Home.aspx"
}
For a windows forms the answer is similar. But you have wo pick a form.
I.e. new FormAdminHome().ShowDialog()
I think that you answered your own question. Just add the POSITION to your query, then just say something like:
If dr("POSITION")="JANITOR" Then
//Go to janitor site
ElseIf ...
...
End If
Like the others have said though, you really shouldn't be passing passwords around like that. Is there actually a reason you're returning the password? If the query even returns anything the user is "Authenticated", so why even return it?