4

I currently have a website with a normal registration and login, coded with ASP.net. I am using an Access database, while using a C# class my friend wrote for handling most of the database actions (executeQuery, executeRead, isExits...).

Now that I've almost finished building my website, I want to start adding security - mostly to my database. I have searched for a while now for a tutorial on the subject, but I could not find anything good exept an old microsoft msdn article which I couldn't realy get its code to work. The furthest I've got now is just no allowing any dangerous characters in the username and password, (such as ',--,;), but it kind of feels as if it is the worse solution that i can use (why shouldn't my users use this characters?).

I think that the best solution I've found is somehow insertion the variables into the query string after declaring it (something to do with "WHERE username=@user" or something like that), but i couldn't get it to work with Access and with my oleDBManager.

here is my current registration code. handle() is removing all ' from the string, and Validate() checks for dangerous parts in the string.

        string username = user.Text;
        string password = pass.Text;
        bool isThingy = false;
        if (handle(ref password)) isThingy = true;
        if (handle(ref username)) isThingy = true;

        if (username != "" && username != null)
        {
            if (password != "" && password != null)
            {
                if (Validate(username, password))
                {
                    if ((db.IsExist("SELECT * FROM Table1 WHERE username='" + username + "'") == false))
                    {
                        int a = db.ExecuteQuery("INSERT INTO `Table1`(`username`, `password`, `logins`, `email`, `fname`, `lname`, `country`, `city`, `birthday`, `userid`) VALUES ('" + username + "', '" + password + "', '0', '', '', '', '', '', '', '" + Convert.ToString(Convert.ToInt32(db.ExecuteCellRead("SELECT MAX(userid) FROM Table1")) + 1) + "');");

                        if (!isThingy) errorLabel.Text = "Your user has been successfully registered";
                        else errorLabel.Text = "The ' token is invalid. your user was registered absence the '.";
                    }
                    else
                        errorLabel.Text = "This username is already taken";
                }
                else errorLabel.Text = "Invalid name format";

            }
            else errorLabel.Text = "Please enter a password";
        }
        else errorLabel.Text = "Please enter a user name";

as for the oleDBManager (named db in my code):

    private OleDbConnection link; // The link instance
    private OleDbCommand command; // The command object
    private OleDbDataReader dataReader; // The data reader object
    private OleDbDataAdapter dataAdapter; // the data adapter object
    private DataTable dataTable; // the data table object
    private string dbName; // the Database filename
    private int version; // the usersTableG office version
    private string connectionString; // the connection string for the database connection
    private string provider; // the matching driver string for the connection string
    private string path; // the path to the database file


...


    public int ExecuteQuery(string query)
    {
        this.link.Open();
        int rowsAffected;
        // ---
        this.command = new OleDbCommand(query, this.link);
        try
        {
            rowsAffected = this.command.ExecuteNonQuery();
        }
        catch (InvalidOperationException e)
        {
            if (e.Data == null)
                throw;
            else
                rowsAffected = -1;
        }
        finally
        {
            this.command.Dispose();
            this.link.Close();
        }
        // ---
        return rowsAffected;
    }

    public bool IsExist(string query)
    {
        this.link.Open();
        // ---
        this.command = new OleDbCommand(query, this.link);
        this.dataReader = this.command.ExecuteReader();
        bool a = this.dataReader.Read();
        // ---
        this.command.Dispose();
        this.link.Close();
        // ---
        return a;
    }

    public string ExecuteCellRead(string query)
    {
        string output = "";
        this.dataTable = this.ExcecuteRead(query);

        foreach (DataRow row in this.dataTable.Rows)
        {
            foreach (object obj in row.ItemArray)
            {
                output += obj.ToString();
            }
        }

        return output;
    }

So, as you might see, the main problem is that the user now can not use characters as '. It suppose the best solution would be using the @ variables in the SQL queries, but I have no idea how.

[thanks for your help] PS. i HAVE changed my tables' name ;)

edit: most of you are telling me to use these parameterized queries, but it would be great if you could give me an example of how to use them, since i've never done that



So, thanks to @Remou, my FINAL code is:

    db.DoWeirdStackOverFlowStuff(
    "INSERT INTO `Table1`(`username`, `password`, `logins`) VALUES (@username, @password, '0');"
    , new string[] { "@username", "@password" }
    , new string[] { username, password });

and

    public int DoWeirdStackOverFlowStuff(string query, string[] vars, string[] reps)
    {
        this.link.Open();
        int rowsAffected;
        // ---
        this.command = new OleDbCommand();
        this.command.CommandText = query;
        this.command.CommandType = System.Data.CommandType.Text;
        this.command.Connection = this.link;

        //Parameters in the order in which they appear in the query
        for (int i = 0; i < vars.Length; i++)
            this.command.Parameters.AddWithValue(vars[i], reps[i]);

        try
        {
            rowsAffected = this.command.ExecuteNonQuery();
        }
        catch (InvalidOperationException e)
        {
            if (e.Data == null)
                throw;
            else
                rowsAffected = -1;
        }
        finally
        {
            this.command.Dispose();
            this.link.Close();
        }
        // ---
        return rowsAffected;
    }

for whoever needs this =]

Noam Gal
  • 1,114
  • 2
  • 11
  • 20
  • 1
    Visual Studio and online MSDN both have simple examples of parameterized sql commands. – Igor Mar 14 '13 at 16:05
  • Here's a intro to SQL injection prevention https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet (I know it's not .NET specific, but the principals are the same) – BLSully Mar 14 '13 at 16:06
  • 2
    Use parameterized sql commands. Blacklist chars will not prevent you from sql injection. And you should save only password hashes in your database. Not the passwords them selfs. And use a salt to protect them. – Peter Mar 14 '13 at 16:09
  • This `SELECT MAX(userid) FROM Table1")) + 1)` is horribly unsafe. Why not use an autonumber? – Fionnuala Mar 14 '13 at 16:28
  • Also, why have the sql in your code? Create the query in MS Access with parameters and refer to the query name `cmd.CommandText = "CreateUser"` `cmd.CommandType = adCmdStoredProc` – Fionnuala Mar 14 '13 at 16:30
  • @Remou i had to change it when i've just started i had to change something and i couldn't change it back to autonumber. (i will in a minute, thank you). for the 2nd comment, well.. I don't really know how =] – Noam Gal Mar 14 '13 at 16:33

2 Answers2

1

Some notes

In MS Access, I have a saved query called UpdateUser, it looks like this:

       UPDATE INTERNETSETTINGS 
       SET url = [@url], 
           databasename = [@databasename], 
           port = [@port], 
           username = [@username],
           [password] = [@password]

I can refer to this query by name in my code, using a command object:

        OleDbCommand Command = new OleDbCommand();

        Command.CommandText = "UpdateUser"; //saved query
        Command.CommandType = System.Data.CommandType.StoredProcedure;
        Command.Connection = cn; //a connection to the database

        //Parameters in the order in which they appear in the query
        Command.Parameters.AddWithValue("@url", "a"); //a,b,c etc for my test run
        Command.Parameters.AddWithValue("@databasename", "b");
        Command.Parameters.AddWithValue("@port","c");
        Command.Parameters.AddWithValue("@username", "d");
        Command.Parameters.AddWithValue("@password", "e");

        Command.ExecuteNonQuery();
Fionnuala
  • 90,370
  • 7
  • 114
  • 152
  • I haven't really gotten into this part of the web programming since i've had the ready C# class to do all that for me, so I'm sorry if i am a little bit slow. Where does the query go in the code you wrote? (shoulf i write it instead of the "UpdateUser"?) thanks for the quick help by the way also, the "a","b","c" and those others should be replaced with the real variables, right (just checking) – Noam Gal Mar 14 '13 at 16:53
  • 1
    I have added a few extra notes. You can either have sql strings or saved queries for CommandText, you will need to use the appropriate CommandType. Saved queries are best, because you can test them and they do not clutter up your code. Just use the query name. As for parameters, the names do not matter, except as prompts for you, but you must get them in the right order. As you say, a,b,c etc are for testing, and I suggest you do something similar until it works the way you want. – Fionnuala Mar 14 '13 at 17:06
  • thanks again for your help, but, one more question - when i run the code it breaks on the `Command.ExecuteNonQuery();` line, saying "Expected query name after EXECUTE". I will add my current code in the question in a minute. – Noam Gal Mar 14 '13 at 17:18
  • 1
    You have not changed the command type, it is not a stored procedure because you are feeding in an SQL string so: `command.CommandType = System.Data.CommandType.Text;` – Fionnuala Mar 14 '13 at 17:54
  • YES, working! thanks for everything and for the quick well explained answers =] – Noam Gal Mar 14 '13 at 17:58
  • Just for general knowledge, can someone still SQL inject my website despite those parameter variables ? (and, well, how? =] ) – Noam Gal Mar 14 '13 at 18:58
  • Like so : http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html You should not really be using MS Access for a website without Sharepoint, but it is far less vulnerable than the bigger lads. – Fionnuala Mar 14 '13 at 19:55
  • but then again, I have my form set on Post, not on get (while in your link they're using the adress line).Do you recomend me switching to SQL database or to SharePoint (whatever it is, hey but i do know it's an MS Office program..!) – Noam Gal Mar 15 '13 at 05:09
0

I don't remember whether Access does the same thing as SQL Server here, but in SQL Server you can escape the single quote mark by doubling it:

username = username.Replace("'", "''");

So you can include single-quote marks in the string, you can store them in the database, and they can't be used as malicious string terminators.

criticalfix
  • 2,870
  • 1
  • 19
  • 32
  • but then again the username changes (lets say from "joe's" to "joe''s", which isn't very good – Noam Gal Mar 14 '13 at 16:17
  • Actually - it doesn't. What is written to the database field is "joe's". But then you have to remember to do the replacement every time you use this value in sql. Still, parameterized queries is the correct way to solve this. – Igor Mar 14 '13 at 16:21
  • This methodology, while better than nothing, is still beatable, according to some - check out [this answer and links](http://stackoverflow.com/a/139810/111266) where some kind of funky Unicode hack nullifies the quote-escaping technique. – Joe Enos Mar 14 '13 at 16:41
  • The U+02BC hack is cute, but it wouldn't work here because there's no implicit cast to Ascii. Escaping the quotes is reasonable. – criticalfix Mar 15 '13 at 15:56