SQL injection-proofing TextBoxes - sql

I've found some tutorials on this already, but they aren't exactly what I'm looking for, I can use the following for username fields and password fields
Private Sub UsernameTextBox_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles UsernameTextBox.KeyPress
If Char.IsDigit(e.KeyChar) OrElse Char.IsControl(e.KeyChar) OrElse Char.IsLetter(e.KeyChar) Then
e.Handled = False
Else
e.Handled = True
End If
End Sub
But for an email field how would I go about protecting against SQL injection for that textbox, as some email accounts have periods or dashes in them?
Update:
Below is an example of an insert statement I use.
Dim con As SqlConnection
con = New SqlConnection()
Dim cmd As New SqlCommand
Try
con.ConnectionString = "Data Source=" & Server & ";Initial Catalog=" & Database & ";User ID=" & User & ";Password=" & Password & ";"
con.Open()
cmd.Connection = con
cmd.CommandText = "INSERT INTO TB_User(STRUserID, password, Email) VALUES('" & UsernameTextBox.Text & "', '" & MD5Hash(PasswordTextBox.Text) & "', '" & EmailTextBox.Text & "')"
cmd.ExecuteNonQuery()
Catch ex As Exception
MessageBox.Show("Error while inserting record on table..." & ex.Message, "Insert Records")
Finally
con.Close()
End Try
So I need to run this with parametrized queries rather than how I'm doing it now?

Instead of filtering out "invalid" data from user input, consider using parametrized queries and not putting user input directly into your queries; that's very bad form.
To run your current query using parameters, it's pretty easy:
Dim con As New SqlConnection()
Dim cmd As New SqlCommand()
Try
con.ConnectionString = "Data Source=" & Server & ";Initial Catalog=" & Database & ";User ID=" & User & ";Password=" & Password & ";"
con.Open()
cmd.Connection = con
cmd.CommandText = "INSERT INTO TB_User(STRUserID, password, Email) VALUES(#username, #password, #email)"
cmd.Parameters.Add("#username", SqlDbType.VarChar, 50).Value = UsernameTextBox.Text
cmd.Parameters.Add("#password", SqlDbType.Char, 32).Value = MD5Hash(PasswordTextBox.Text)
cmd.Parameters.Add("#email", SqlDbType.VarChar, 50).Value = EmailTextBox.Text
cmd.ExecuteNonQuery()
Catch ex As Exception
MessageBox.Show("Error while inserting record on table..." & ex.Message, "Insert Records")
Finally
con.Close()
End Try
All you have to do is use cmd.Parameters.Add with a parameter name and the right database type (the ones I guessed probably don't match up, so you'll want to change them), then set the value to the value you want used in the query. Parameter names start with an #.

It doesn't depend on the textbox. Don't compose a sql sentence joining strings like this:
"SELECT * FROM User WHERE UserName=" + tbName.Text + ...
Use stored procedures or parameterized queries and you'll be safe from SQL injection.
When you use parameters, the textbox content is used as a value, so it doesn't matter what it contains.

Use a parametrized query like this:
Using conn = New SqlConnection("some connection string")
Using cmd = New SqlCommand("SELECT Password FROM tblUser WHERE UserName = #Name", conn)
cmd.Parameters.Add(New SqlParameter("Name", UsernameTextBox.Text))
conn.Open()
Dim password As String = DirectCast(cmd.ExecuteScalar(), String)
Console.WriteLine(password)
End Using
End Using
This is injection safe!

Related

Data type mismatch in criteria expression while updating password field

This is my Select & Update code for OLEDB DB.
I am getting a Data type mismatch in criteria expression error whilst changing the Password field value.
All four fields are set to Long Text datatype.
Update Query
con = Class1.dbconn
cmd = New OleDbCommand("Update User_details set User_ID ='" & TextBox1.Text & "', User_Name='" & TextBox2.Text & "', [Password]='" & TextBox3.Text & "' where Sno='" & Label4.Text & "'", con)
cmd.ExecuteNonQuery()
MessageBox.Show("User Details Updated")
Select Query
cmd = New OleDbCommand("select * from User_details where User_ID='" & TextBox1.Text & "'", con)
Dim dr As OleDbDataReader
dr = cmd.ExecuteReader
If dr.Read Then
Label4.Text = dr("Sno").ToString
TextBox2.Text = dr("User_Name").ToString
TextBox3.Text = dr("Password").ToString
TextBox2.Text = TextBox2.Text.Replace(" ", "")
TextBox3.Text = TextBox3.Text.Replace(" ", "")
dr.Close()
End If
Keep your database objects local so you can control when they are closed and disposed. Using...End Using blocks take care of this for you even if there is an error. The Using blocks demonstrated here take care of both the connection and the command. Note the comma after the connection line.
Always use Parameters. Not only does it make your command text easier to read and write (without all the quotes, double quotes and ampersands) but it protects your database from the destruction of Sql injection. When you are using the OleDb provider it is essential that order that the parameters appear in the command text match the order they are added to the parameters collection. Unlike Sql Server, Access pays no attention to the names of the parameters; only the order.
Notice that the connection is not opened until right before the .Execute... and is closed (with the End Using) directly after. Connections are precious resources. I used a DataTable instead of a DataReader in the SelectUser sub so I could close the connection before updated the user interface. In the UpdatePassword sub the connection is closed before showing the MessageBox. After all the end user could have gone to lunch and there would be your connection flapping in the breeze.
As far as the type mis-match check the links provided by #Jimi and then check your database to see if the OleDbType matches.
Private Sub UpdatePassword()
Using con As New OleDbConnection("Your connection string"),
cmd As New OleDbCommand("Update User_details set User_ID = #ID, User_Name = #Name, [Password]= #Password Where Sno= #Sno;", con)
With cmd.Parameters
.Add("#ID", OleDbType.LongVarChar).Value = TextBox1.Text
.Add("#Name", OleDbType.LongVarChar).Value = TextBox2.Text
.Add("#Password", OleDbType.LongVarChar).Value = TextBox3.Text
.Add("#Sno", OleDbType.LongVarChar).Value = Label4.Text
End With
con.Open()
cmd.ExecuteNonQuery()
End Using
MessageBox.Show("User Details Updated")
End Sub
Private Sub SelectUser()
Dim dt As New DataTable
Using con As New OleDbConnection("Your connection string"),
cmd As New OleDbCommand("select * from User_details where User_ID= #ID;", con)
cmd.Parameters.Add("#ID", OleDbType.LongVarChar).Value = TextBox1.Text
con.Open()
dt.Load(cmd.ExecuteReader)
End Using
If dt.Rows.Count > 0 Then
Dim row As DataRow = dt.Rows(0)
Label4.Text = row("Sno").ToString
TextBox2.Text = row("User_Name").ToString
TextBox3.Text = row("Password").ToString
TextBox2.Text = TextBox2.Text.Replace(" ", "")
TextBox3.Text = TextBox3.Text.Replace(" ", "")
End If
End Sub
Finally, you should NEVER store passwords as plain text. They should be salted and hashed. I will leave it to you to research how to do this.

How to close the sqldatareader within Using statement?

I'd like to use this code to verify if duplication occurs or not before saving the data to the database. How am I supposed to close the sqldatareader? (As what the error shows me)
con.ConnectionString = "Data Source=PC85AAIEw\SQLEXPRESS;Initial Catalog=Student;Integrated Security=True"
cmd.Connection = con
con.Open()
Dim theQuery As String = "SELECT * FROM Profile WHERE RollNo=#RollNo AND Name=#Name"
Dim cmd1 As SqlCommand = New SqlCommand(theQuery, con)
cmd1.Parameters.AddWithValue("#RollNo", TextBox1.Text)
cmd1.Parameters.AddWithValue("#Name", TextBox2.Text)
Using reader As SqlDataReader = cmd1.ExecuteReader()
If reader.HasRows Then
MessageBox.Show("User already registered! Please try again.", "Error", MessageBoxButtons.OK)
Else
cmd.CommandText = "INSERT INTO Profile VALUES ('" & rollno & "' , '" & name & "' , '" & gender & "' , '" & address & "' , '" & phoneno & "' , '" & datereg & "' , '" & faculty & "' , '" & course & "' , '" & semester & "')"
MessageBox.Show("Profile has been successfully registered!", "Thank you", MessageBoxButtons.OK)
i = cmd.ExecuteNonQuery()
End If
End Using
con.Close()
The error are referring to is because you must complete the execution of the data reader before you try to execute another command on the same connection.
Additionally there are some issues with your code:
It is strongly recommended you use and then dispose of SqlConnections as you use them, do not try to reuse these globally in your application. The ado.net SQL Server client library will handle connection pooling for you by default.
You need to use parameters with your insert just like you did on your select.
Do not to use AddWithValue when adding your parameters, instead use the constructor and also specify the sql data type. If RollNo is a number (like integer) then you should pass the value as an integer to your parameter. I assumed it was a string stored in a varchar.
Wrap all types that implement IDisposable in Using statements to ensure resources are always released. (In case any one wants to nitpick, no it is not required for SqlCommand in this case.)
Dim recordExists As Boolean
Using con As SqlConnection = New SqlConnection("Data Source=PC85AAIEw\SQLEXPRESS;Initial Catalog=Student;Integrated Security=True")
Using cmd As SqlCommand = New SqlCommand("SELECT RollNo FROM Profile WHERE RollNo=#RollNo AND Name=#Name", con)
cmd.Parameters.Add("#RollNo", SqlDbType.VarChar).Value = TextBox1.Text
cmd.Parameters.Add("#Name", SqlDbType.VarChar).Value = TextBox2.Text
con.Open()
Using reader As SqlDataReader = cmd.ExecuteReader()
recordExists = reader.HasRows
End Using
End Using
End Using
If recordExists Then
MessageBox.Show("User already registered! Please try again.", "Error", MessageBoxButtons.OK)
Else
Using con As SqlConnection = New SqlConnection("Data Source=PC85AAIEw\SQLEXPRESS;Initial Catalog=Student;Integrated Security=True")
Using cmd As SqlCommand = New SqlCommand("INSERT INTO Profile (RollNo, Name) VALUES (#RollNo, #Name)", con)
cmd.Parameters.Add("#RollNo", SqlDbType.VarChar).Value = TextBox1.Text
cmd.Parameters.Add("#Name", SqlDbType.VarChar).Value = TextBox2.Text
con.Open()
cmd.ExecuteNonQuery()
MessageBox.Show("Profile has been successfully registered!", "Thank you", MessageBoxButtons.OK)
End Using
End Using
End If
if you are using using then not need to close. because it internally close all connection. The code would be like this
using(var con=new Sqlconnection("Data Source=PC85AAIEw\SQLEXPRESS;Initial Catalog=Student;Integrated Security=True")){
cmd.Connection = con
con.Open()
Dim theQuery As String = "SELECT * FROM Profile WHERE RollNo=#RollNo AND Name=#Name"
Dim cmd1 As SqlCommand = New SqlCommand(theQuery, con)
cmd1.Parameters.AddWithValue("#RollNo", TextBox1.Text)
cmd1.Parameters.AddWithValue("#Name", TextBox2.Text)
Using reader As SqlDataReader = cmd1.ExecuteReader()
If reader.HasRows Then
MessageBox.Show("User already registered! Please try again.", "Error", MessageBoxButtons.OK)
Else
cmd.CommandText = "INSERT INTO Profile VALUES ('" & rollno & "' , '" & name & "' , '" & gender & "' , '" & address & "' , '" & phoneno & "' , '" & datereg & "' , '" & faculty & "' , '" & course & "' , '" & semester & "')"
MessageBox.Show("Profile has been successfully registered!", "Thank you", MessageBoxButtons.OK)
i = cmd.ExecuteNonQuery()
End If
End Using
con.Close()}

ExecuteReader: CommandText property has not been initialized when trying to make a register form for my database

hello guys im trying to script a register form for my database and i came with this code >> so can anyone help ?
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 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 & "')"
cn.Open()
dr = cmd.ExecuteReader
If dr.HasRows Then
MsgBox("You're already registered")
Else
MsgBox("Already registered")
End If
End Sub
Edit your Code in this way..
cmd.CommandText = "INSERT INTO User_Data(Username,Password) VALUES('" & TextBox1.Text & "' , '" & TextBox2.Text & "')"
cn.Open()
cmd.ExecuteNonQuery()
cn.Close()
Insert will not retrieve any records it's a SELECT statement you want to use .I'll suggest you use stored procedures instead to avoid Sql-Injections.
ExecuteReader it's for "SELECT" queries, that helps to fill a DataTable. In this case you execute command before cmd.commandText is defined.
You should have define cmd.commandText before and use ExecuteNonQuery after, like this.
Dim cn As New SqlConnection
Dim cmd As New SqlCommand
cn.ConnectionString = "Server=localhost;Database=test;Uid=sa;Pwd=fadyjoseph21"
cmd.Connection = cn
cn.Open()
cmd.CommandText = "INSERT INTO User_Data(Username,Password) VALUES('" & TextBox1.Text & "','" & TextBox2.Text & "')"
cmd.ExecuteNonQuery()
cn.Close()
cmd.CommandText should be assigned stored proc name or actual raw SQL statement before calling cmd.ExecuteReader
Update:
Change code as follows
....
cmd.Connection = cn
cmd.CommandText = "select * from TblToRead where <filter>" ''This is select query statement missing from your code
cn.Open()
dr = cmd.ExecuteReader ....
where <filter> will be something like username = "' & Request.form("username') & '" '
The error itself is happening because you're trying to execute a query before you define that query:
dr = cmd.ExecuteReader
'...
cmd.CommandText = "INSERT INTO User_Data(Username,Password) VALUES('" & TextBox1.Text & "' and '" & TextBox2.Text & "')"
Naturally, that doesn't make sense. You have to tell the computer what code to execute before it can execute that code:
cmd.CommandText = "INSERT INTO User_Data(Username,Password) VALUES('" & TextBox1.Text & "' and '" & TextBox2.Text & "')"
'...
dr = cmd.ExecuteReader
However, that's not your only issue...
You're also trying to execute a DataReader, but your SQL command doesn't return data. It's an INSERT command, not a SELECT command. So you just need to execute it directly:
cmd.CommandText = "INSERT INTO User_Data(Username,Password) VALUES('" & TextBox1.Text & "' and '" & TextBox2.Text & "')"
cmd.ExecuteNonQuery
One value you can read from an INSERT command is the number of rows affected. Something like this:
cmd.CommandText = "INSERT INTO User_Data(Username,Password) VALUES('" & TextBox1.Text & "' and '" & TextBox2.Text & "')"
Dim affectedRows as Int32 = cmd.ExecuteNonQuery
At this point affectedRows will contain the number of rows which the query inserted successfully. So if it's 0 then something went wrong:
If affectedRows < 1 Then
'No rows were inserted, alert the user maybe?
End If
Additionally, and this is important, your code is wide open to SQL injection. Don't directly execute user input as code in your database. Instead, pass it as a parameter value to a pre-defined query. Basically, treat user input as values instead of as executable code. Something like this:
cmd.CommandText = "INSERT INTO User_Data(Username,Password) VALUES(#Username,#Password)"
cmd.Parameters.Add("#Username", SqlDbType.NVarChar, 50).Value = TextBox1.Text
cmd.Parameters.Add("#Password", SqlDbType.NVarChar, 50).Value = TextBox2.Text
(Note: I guessed on the column types and column sizes. Adjust as necessary for your table definition.)
Also, please don't store user passwords as plain text. That's grossly irresponsible to your users and risks exposing their private data (even private data on other sites you don't control, if they re-use passwords). User passwords should be obscured with a 1-way hash and should never be retrievable, not even by you as the system owner.
You're attempting to change the CommandText after you're executing your query.
Try this:
Private cn = New SqlConnection("Server=localhost;Database=test;UID=sa;PWD=secret")
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim cmd As New SqlCommand
cmd.CommandText = "select * from table1" ' your sql query selecting data goes here
Dim dr As SqlDataReader
cmd.Connection = cn
cn.Open()
dr = cmd.ExecuteReader
If dr.HasRows = 0 Then
InsertNewData(TextBox1.Text, TextBox2.Text)
Else
MsgBox("Already registered")
End If
End Sub
Private Sub InsertNewData(ByVal username As String, ByVal password As String)
Dim sql = "INSERT INTO User_Data(Username,Password) VALUES(#Username, #Password)"
Dim args As New List(Of SqlParameter)
args.Add(New SqlParameter("#Username", username))
args.Add(New SqlParameter("#Password", password))
Dim cmd As New SqlCommand(sql, cn)
cmd.Parameters.AddRange(args.ToArray())
If Not cn.ConnectionState.Open Then
cn.Open()
End If
cmd.ExecuteNonQuery()
cn.Close()
End Sub
This code refers the INSERT command to another procedure where you can create a new SqlCommand to do it.
I've also updated your SQL query here to use SqlParameters which is much more secure than adding the values into the string directly. See SQL Injection.
The InsertNewData method builds the SQL Command with an array of SQLParameters, ensures that the connection is open and executes the insert command.
Hope this helps!

Insert into database in vb not working

I have the below snippet of code whenever I try registering the username exist part seems to be executed even for a username that doesn't exist in the database. Don't know where am wrong here any help will be appreciated.
'Connecting to SQL Database and executing Query------------------------------------------
Dim Strconn As String = "Data Source=.\SQLEXPRESS; Database=QuizDB; Integrated Security = true"
Dim Strcmd As String = "INSERT INTO reg_info(uname,pass,fname,lname,dob,course,college) VALUES ('" & user_name.Text & "','" & con_pass.Text & "', '" & first_name.Text & "', '" & last_name.Text & "', '" & dob.Text & "', '" & course.Text & "', '" & college.Text & "');"
Dim da As New SqlDataAdapter
Dim ds As New DataSet
Dim sqlcmd As SqlCommand
sqlconn = New SqlConnection(Strconn)
Try
sqlconn.Open()
Catch ex As Exception
MsgBox("Could not connect to DataBase. Application will close now!", vbCritical, "Database Error")
End
End Try
sqlcmd = New SqlCommand(Strcmd, sqlconn)
da.SelectCommand = sqlcmd
sqlcmd.Dispose()
sqlconn.Close()
'Exception Handling-----------------------
Dim exc As Exception = Nothing
Try
da.Fill(ds)
Catch ex As Exception
exc = ex
Finally
If Not (exc) Is Nothing Then
MsgBox("User Name Already Exist. Please select a different User Name!", vbExclamation, "Already Exist")
user_name.Focus()
Else
MsgBox("Registration Successful.", vbInformation, "Successful")
Me.Close()
Login.Show()
End If
End Try
Here is a refactor of your code with some helpful guidance. I think this will compile, but if it does not, then you can do a little homework to figure out what is missing.
Try
' the USING block guarantees that the object's Close() and Dispose() methods are fired automatically when you exit the block
Using sqlconn As New SqlConnection("Data Source=.\SQLEXPRESS; Database=QuizDB; Integrated Security = true")
Using sqlcmd As SqlCommand = sqlconn.CreateCommand
With sqlcmd
.CommandType = CommandType.Text
' parameterized query to protect against SQL injection
.CommandText = "INSERT INTO reg_info(uname,pass,fname,lname,dob,course,college) VALUES (#username, #password, #firstname, #lastname, #dob, #course, #college)"
With .Parameters
.Clear()
.AddWithValue("#username", user_name.Text)
.AddWithValue("#password", con_pass.Text)
.AddWithValue("#firstname", first_name.Text)
.AddWithValue("#lastname", last_name.Text)
.AddWithValue("#dob", dob.Text)
.AddWithValue("#course", course.Text)
.AddWithValue("#college", college.Text)
End With
.ExecuteScalar() ' Actually executes the SQL command
End With
End Using
End Using
MsgBox("Registration successful")
Catch ex As Exception
' Any error in the TRY block will automatically jump to herem and the "ex" object will be an Exception object with populated properties
MsgBox("User name already exists. Error from database is " & ex.Message)
End Try

Error in vb.net code in INSERT INTO

When I try to insert data in these three field gets an error saying error in INSERT INTO Statement.
but when a save in only the first field sname it gets added but when adds other two gets this error
I am getting an exception in INSERT INTO Statement check below
any advice?
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Try
Dim dbprovider As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\Taher\Documents\Visual Studio 2010\Projects\WindowsApplication1\WindowsApplication1\Database1.accdb;Persist Security Info=False;"
Me.con = New OleDb.OleDbConnection()
con.ConnectionString = dbprovider
con.Open()
Dim sqlquery As String = "INSERT INTO admin (sname,username,password)" + "VALUES ('" & txtname.Text & "','" & txtuser.Text & "','" & txtpass.Text & "');"
Dim sqlcommand As New OleDb.OleDbCommand(sqlquery)
With sqlcommand
.CommandText = sqlquery
.Connection = con
.ExecuteNonQuery()
con.Close()
End With
MsgBox("User Registered")
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
The word PASSWORD is a reserved keyword in JET-SQL for Microsoft Access. If you have a column with that name you should encapsulate it with square brackets
"INSERT INTO admin (sname,username,[password])" &% _
"VALUES ('" & txtname.Text & "','" & txtuser.Text & _
"','" & txtpass.Text & "');"
That's the reason of the syntax error, however let me tell you that building sql commands concatenating strings is a very bad practice. You will have problems when your values contain single quotes and worst of all, your code could be used for sql injection Attacks
So your code should be changed in this way
Dim sqlquery As String = "INSERT INTO admin (sname,username,password)" & _
"VALUES (?, ?, ?)"
Dim sqlcommand As New OleDb.OleDbCommand(sqlquery)
With sqlcommand
.CommandText = sqlquery
.Connection = con
.Parameters.AddWithValue("#p1", txtname.Text)
.Parameters.AddWithValue("#p2", txtuser.Text)
.Parameters.AddWithValue("#p3", txtpass.Text)
.ExecuteNonQuery()
con.Close()
End With
also your use of the object OleDbConnection doesn't follow a good pattern. In case of exception you don't close the connection and this could be a problem in reusing the connection in subsequent calls.
You should try to use the Using statement
Using connection = New OleDb.OleDbConnection()
connection.ConnectionString = dbprovider
connection.Open()
.....
' rest of command code here '
' No need to close the connection
End Using
in this way, also if you get an exception the OleDbConnection will be closed and disposed without impact on system resource usage.