-1

I'm trying to make a login and register with parameter with this code. Can anyone help me to prevent sql injection on vb.net?

This is my login

mysqlConn.Open()
    sqlCmd = New Odbc.OdbcCommand
    sqlCmd.Connection = mysqlConn
    sqlCmd.CommandText = "SELECT * FROM tbl_user WHERE username = '" & Replace(TextBox01.Text, "'", "''") & "' AND password = '" & Replace(TextBox02.Text, "'", "''") & "' "
    dr = sqlCmd.ExecuteReader
    dr.Read()

    Try
        getUsername = dr("username")
        MessageBox.Show("You are now login!", "System", MessageBoxButtons.OK, MessageBoxIcon.Information)
        Me.Hide()
        frmMenu.Show()

        TextBox01.Text = ""
        TextBox02.Text = ""
        mysqlConn.Close()
    Catch ex As Exception
        MessageBox.Show("Invalid Username or Password!", "System", MessageBoxButtons.OK, MessageBoxIcon.Error)
        TextBox01.Focus()
    End Try

Then this is my register

mysqlConn.Open()
    Dim myQuery As String
    myQuery = "INSERT INTO tbl_user(firstname, lastname, address, email, username, password) VALUES ('" & Replace(Trim(TextBox01.Text), "'", "''") & "', '" & Replace(Trim(TextBox02.Text), "'", "''") & "', '" & Replace(Trim(TextBox03.Text), "'", "''") & "', '" & Replace(Trim(TextBox04.Text), "'", "''") & "', '" & Replace(Trim(TextBox05.Text), "'", "''") & "', '" & Replace(Trim(TextBox06.Text), "'", "''") & "')"

    sqlCmd = New Odbc.OdbcCommand
    sqlCmd.Connection = mysqlConn
    sqlCmd.CommandText = myQuery
    dr = sqlCmd.ExecuteReader
    mysqlConn.Close()
    MessageBox.Show("You are now register!", "System", MessageBoxButtons.OK, MessageBoxIcon.Information)
    Me.Hide()
    frmMenu.Hide()
    frmLogin.Show()
  • 3
    Possible duplicate of [How do I create a parameterized SQL query? Why Should I?](https://stackoverflow.com/questions/542510/how-do-i-create-a-parameterized-sql-query-why-should-i) – MatSnow Jun 28 '18 at 12:30
  • 2
    As a general disclaimer: You should avoid to work with passwords in plain text! You could use sodium.net for a strong and easy way to hash and verify passwords – Marco Sadowski Jun 28 '18 at 12:38

2 Answers2

2

Use parameterized queries to avoid SQL Injection. For example:

MyCommand = New SqlCommand("SELECT * FROM tbl_user WHERE username=@username AND password=@password", myconnection)
MyCommand.Parameters.Add("@username", SqlDbType.VarChar, 50).Value = strUsername
MyCommand.Parameters.Add("@password", SqlDbType.VarChar, 50).Value = strPassword

Note: strUsername & strPassword are the parameters you are passing to the function.

Rami Zebian
  • 539
  • 1
  • 5
  • 23
1

DISCLAIMER

Please note that I have NOT actually tested any of the code below, and it may require some additional modifications to work in your environment.


I agree with @RamiZebian with regards to implementing the use of parameterized queries to help prevent SQL injection. The relatively simple queries you gave as examples could be implemented rather quickly in the way he describes, but I believe it will require one minor modification. Since you appear to be using Odbc, I believe you'll need to change the @ to a question mark (?). This would change his example to look more like this (if I have it correct - I don't use the Odbc connector often):

MyCommand = New SqlCommand("SELECT * FROM tbl_user WHERE username=?username AND password=?password", myconnection)
MyCommand.Parameters.Add("?username", SqlDbType.VarChar, 50).Value = strUsername
MyCommand.Parameters.Add("?password", SqlDbType.VarChar, 50).Value = strPassword

Even with this minor change, his fairly direct method should work and get you up and running with minimal extra work.

TL;DR - Use the method @RamiZebian proposed for quick implementation. The rest of this answer describes additional measures you can take if you have the time and means to improve your protection against SQL injection as well as other potential issues when programming for database communication.


That being said, however, if you have the ability, you might also want to consider taking things "one step further". In this case, my suggestion would be, if you have access to and control of the MySQL server (or, at least sufficient privileges), to create a stored routine (procedure and/or function). It requires a bit more setup, but using this method comes with the following benefits:

  • It helps to take the burden of the logic out of your application so that you don't necessarily have to change your programming any time there are changes to the database.
  • This method also can provide an added layer of security (if needed) by allowing or preventing certain users or groups access to the routine through the database permissions rather than controlling it in your programming.
  • You don't have to worry so much about escape characters like the apostrophe (') as these are internally handled by the database.
  • Additionally, in the cases of long and complex SQL queries, it can help simplify your programming by allowing you to refer to the routine instead of constructing the entire query in your code.
  • One final benefit of this method would be that the database itself is able to handle some of the data validation instead of you having to write code to make certain that the values you're passing are "legitimate" for the query.

Check out this answer from another SO question for some clarification on the differences between Procedures and Functions: MySQL stored procedure vs function, which would I use when?

Once you have the stored routine set up in the database, you can then use a parameterized query in your programming to call it. Here's an example. Please note that this is somewhat simplified and just going off of the code from your OP. You may want to include some additional validation within your methods to ensure the data you're getting back is what you expect.


STEP 1: Create the routines you'll need in the MySQL database.

Below you'll see that the exact same queries are being used, but all of the actual "operational" SQL for each is wrapped up in a PROCEDURE.

A) First, for the SELECT statement:

DELIMITER $$
CREATE PROCEDURE `UserSelect`(
    IN inUsername VARCHAR(50),
    IN inPassword VARCHAR(60))
BEGIN
    SELECT *
    FROM tbl_user
    WHERE username = inUsername
        AND password = inPassword;
END$$
DELIMITER ;

B) And then, the INSERT statement for the registration:

DELIMITER $$
CREATE PROCEDURE `UserRegister`(
    IN inFirstName VARCHAR(50),
    IN inLastName VARCHAR(60)),
    IN inAddress VARCHAR(60),
    IN inEMail VARCHAR(60),
    IN inUsername VARCHAR(60),
    IN inPassword VARCHAR(60))
BEGIN
    INSERT INTO tbl_user
        (firstname,
        lastname,
        address,
        email,
        username,
        password)
    VALUES
        (inFirstName,
        inLastName,
        inAddress,
        inEMail,
        inUsername,
        inPassword);

END$$
DELIMITER ;

STEP 2: Write the VB.NET code to use your new stored routines

One thing you might consider at this stage is that, while the Odbc namespace can provide you with most or all of the database access you will currently need, you may find that you get improved performance and utility from the MySQL .NET Connector (Connector/NET). The syntax is virtually identical, but it's designed and optimized to work specifically with a MySQL database/server. I'll make my example here using Odbc to reduce confusion, but I definitely recommend looking into the more provider-specific connector. Also, please remember that this code has not been tested and may very likely require some modification to work in your environment.

A) The "login" method

Try
    mysqlConn.Open()

    sqlCmd = New Odbc.OdbcCommand
    sqlCmd.Connection = mysqlConn
    sqlCmd.CommandType = CommandType.StoredProcedure
    sqlCmd.CommandText = "CALL UserSelect(@Username, @Password)"

    sqlCmd.Parameters.Add("@Username", Odbc.OdbcType.VarChar, 50).Value = TextBox01.Text
    sqlCmd.Parameters.Add("@Password", Odbc.OdbcType.VarChar, 50).Value = TextBox02.Text

    Try
        dr = sqlCmd.ExecuteReader
        dr.Read()

        getUsername = dr("username")
        MessageBox.Show("You are now login!", "System", _
                        MessageBoxButtons.OK, MessageBoxIcon.Information)
        Me.Hide()
        frmMenu.Show()

        TextBox01.Text = ""
        TextBox02.Text = ""
    Catch ex As Exception
        MessageBox.Show("Invalid Username or Password!", "System", _
                        MessageBoxButtons.OK, MessageBoxIcon.Error)
        TextBox01.Focus()
    End Try
Catch ex As Exception
    MessageBox.Show("Database connection error!", "System", _
                    MessageBoxButtons.OK, MessageBoxIcon.Error)
Finally
    If Not mysqlConn Is Nothing Then
        mysqlConn.Close()
        mysqlConn.Dispose()
    End If

    If Not sqlCmd Is Nothing Then sqlCmd.Dispose()
    If Not dr Is Nothing Then dr.Dispose()
End Try

B) And the "register" method

Try
    mysqlConn.Open()

    sqlCmd = New Odbc.OdbcCommand
    sqlCmd.Connection = mysqlConn
    sqlCmd.CommandType = CommandType.StoredProcedure
    sqlCmd.CommandText = "CALL UserRegister(@Firstname, @Lastname, @Address, @Email, @Username, @Password)"

    sqlCmd.Parameters.Add("@Firstname", Odbc.OdbcType.VarChar, 50).Value = TextBox01.Text.Trim
    sqlCmd.Parameters.Add("@Lastname", Odbc.OdbcType.VarChar, 50).Value = TextBox02.Text.Trim
    sqlCmd.Parameters.Add("@Address", Odbc.OdbcType.VarChar, 50).Value = TextBox03.Text.Trim
    sqlCmd.Parameters.Add("@Email", Odbc.OdbcType.VarChar, 50).Value = TextBox04.Text.Trim
    sqlCmd.Parameters.Add("@Username", Odbc.OdbcType.VarChar, 50).Value = TextBox05.Text.Trim
    sqlCmd.Parameters.Add("@Password", Odbc.OdbcType.VarChar, 50).Value = TextBox06.Text.Trim

    Try
        sqlCmd.ExecuteNonQuery
        MessageBox.Show("You are now register!", "System", _
                        MessageBoxButtons.OK, MessageBoxIcon.Information)
        Me.Hide()
        frmMenu.Hide()
        frmLogin.Show()
    Catch ex As Exception
        MessageBox.Show("Registration failed!", "System", _
                        MessageBoxButtons.OK, MessageBoxIcon.Error)
        TextBox01.Focus()
    End Try
Catch ex As Exception
    MessageBox.Show("Database connection error!", "System", _
                    MessageBoxButtons.OK, MessageBoxIcon.Error)
Finally
    If Not mysqlConn Is Nothing Then
        mysqlConn.Close()
        mysqlConn.Dispose()
    End If

    If Not sqlCmd Is Nothing Then sqlCmd.Dispose()
End Try

FINAL THOUGHTS/NOTES

Now, there are some additional changes I might suggest to "optimize" your code, such as moving to the MySQL .NET Connector and possibly some Using statements. Here's a link to a very general example of how to use stored routines with the MySQL .NET Connector: Using Stored Routines from Connector/NET. To update the "login" method above for Connector/NET and add some Using statements to help manage all of the objects:

Imports MySql.Data
Imports MySql.Data.MySqlClient

Private Sub CheckLoginUser()
    Dim TempUsername As String = String.Empty
    Dim ConnectString As New MySqlConnectionStringBuilder()

    With ConnectString
        .Server = <SERVERNAME>
        .Port = <PORTNUMBER>
        .UserID = <USERNAME>
        .Password = <PASSWORD>
    End With

    Using MySQLDB As New MySqlConnection(ConnectString.ConnectionString)
        Using MySQLCmd As New MySqlCommand
            Try
                MySQLDB.Open()

                With MySQLCmd
                    .Connection = MySQLDB
                    .CommandType = CommandType.StoredProcedure
                    .CommandText = "CALL UserSelect(@Username, @Password)"

                    .Parameters.AddWithValue("@Username", TextBox01.Text)
                    .Parameters("@Username").Direction = ParameterDirection.Input

                    .Parameters.AddWithValue("@Password", TextBox02.Text)
                    .Parameters("@Password").Direction = ParameterDirection.Input
                End With

                Dim Reader As MySqlDataReader

                Try
                    Reader = MySQLCmd.ExecuteReader
                    Reader.Read()

                    TempUsername = Reader("username")
                    ' -- Possibly include some additional validation here
                    ' -- e.g., check for null/empty string, etc.

                    MessageBox.Show("You are now login!", "System", _
                                    MessageBoxButtons.OK, MessageBoxIcon.Information)

                    Me.Hide()
                    frmMenu.Show()

                    TextBox01.Text = ""
                    TextBox02.Text = ""
                Catch ex As Exception
                    MessageBox.Show("Invalid Username or Password!", "System", _
                                    MessageBoxButtons.OK, MessageBoxIcon.Error)
                    TextBox01.Focus()
                End Try
            Catch ex As Exception
                MessageBox.Show("Database connection error!", "System", _
                                MessageBoxButtons.OK, MessageBoxIcon.Error)
            End Try
        End Using
    End Using
End Sub

The stored routine for this method could be "optimized" a bit as well to only return the value from the username column since that appears to be the only one you're interested in. Something like this:

DELIMITER $$
CREATE PROCEDURE `UserSelect`(
    IN inUsername VARCHAR(50),
    IN inPassword VARCHAR(60),
    OUT outUsername VARCHAR(50))
BEGIN
    SELECT username INTO outUsername
    FROM tbl_user
    WHERE username = inUsername
        AND password = inPassword;
END$$
DELIMITER ;

This change would change the example code as follows:

                ...
                With MySQLCmd
                    .Connection = mysqlConn
                    .CommandType = CommandType.StoredProcedure
                    .CommandText = "CALL UserSelect(@Username, @Password, @Response)"

                    .Parameters.AddWithValue("@Username", TextBox01.Text)
                    .Parameters("@Username").Direction = ParameterDirection.Input

                    .Parameters.AddWithValue("@Password", TextBox02.Text)
                    .Parameters("@Password").Direction = ParameterDirection.Input

                    .Parameters.AddWithValue("@Response", MySqlDbType.VarChar)
                    .Parameters("@Response").Direction = ParameterDirection.Output
                End With

                ' Not required anymore
                ' Dim Reader As MySqlDataReader

                Try
                    TempUsername = Convert.ToString(MySQLCmd.ExecuteScalar)

                    MessageBox.Show("You are now login!", "System", _
                                    MessageBoxButtons.OK, MessageBoxIcon.Information)
                ...

As a last point, I'd also recommend you try to make use of the internal MySQL users table (mysql.user) instead of creating your own users table. The password storage would be more secure and you'd be able (if you ever found the need) to apply user-level permissions to individual tables, routines, etc.

Even with these caveats, however, the structure presented above - with quite possibly some modification for your specific environment - should work for what you're trying to accomplish.

G_Hosa_Phat
  • 976
  • 2
  • 18
  • 38
  • I will try this later. I don't have my laptop right now. By you mean mysql.net connector I need to change from odbc to this? The one I'm trying to do is a simple program that so other people that trying to use sql injection they can't enter or find an error. That's why I'm trying to use parameter is whenever I enter @@@ in username in login I get an error – ItsMeMaiku Jun 28 '18 at 23:49
  • also I don't know where to put the parameter you say – ItsMeMaiku Jun 29 '18 at 04:22
  • @MikeFrancisFilSajol - Yes, I'm talking about replacing the Odbc connection stuff with the MySQL .NET connector. The code for adding the parameter is included in the examples I provided. As for the error you're getting, a lot more detail will be needed to determine the problem including an example of the code that isn't working. – G_Hosa_Phat Jun 29 '18 at 04:52
  • it's okay now but i'm having problem with the duplicate username – ItsMeMaiku Jun 29 '18 at 05:20
  • If you have duplicate usernames in the database, you'll probably want to do some additional validation when you retrieve the `DataReader` object. Obviously, it would be preferable if there weren't any duplicates, which would require some clean-up of your data and possibly a `UNIQUE` constraint for the `username` column. As I said in my final thoughts, though, unless you are needing some additional fields for other user-specific data or have some other specific reason, I would definitely recommend using the built-in `mysql.user` table instead of creating your own, separate table if possible. – G_Hosa_Phat Jun 29 '18 at 13:42
  • I mean I want to prevent duplicate username when registering – ItsMeMaiku Jun 30 '18 at 03:48
  • Then, I would definitely go back to my recommendation that you either implement a `UNIQUE` constraint on the `username` column in your table or reconsider the need for your own custom user table in favor of the internal `mysql.user` table. – G_Hosa_Phat Jun 30 '18 at 04:25