Securing data before update - asp.net

I have a few text boxes that on my page that can add data to my database. What I'm looking for is how to actually make it more secure. The basic error checking of empty text boxes I know how to check. What I'm really searching for is bad data being saved or special characters like " ' " or " - ". Its a simple application only myself and maybe two other people will use it but still want to make sure learning and coding correctly. Any suggestions. This is my save button.
Here is my code:
try
{
OleDbConnection conn = new OleDbConnection(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=H:\Databases\AddressBook.mdb");
conn.Open();
DataSet ds = new DataSet();
string cmd = "SELECT * FROM tblAddressBook";
OleDbDataAdapter da = new OleDbDataAdapter(cmd, conn);
da.Fill(ds, "Display");
DataRow newRow = ds.Tables["Display"].NewRow();
newRow[1] = txtFirstName.Text;
newRow[2] = txtLastName.Text;
newRow[3] = txtEmail.Text;
newRow[4] = txtPhone.Text;
ds.Tables["Display"].Rows.Add(newRow);
OleDbCommandBuilder cb = new OleDbCommandBuilder(da);
cb.DataAdapter.Update(ds.Tables["Display"]);
conn.Close();
GridView1.DataBind();
}
catch (Exception)
{
lblErrorSave.Text = "Your information did not save clear form and try again";
}

Your code as shown is secure, but does have problems.
What your question is about is SQL Injection. This arises where you use dynamic SQL, like so (air code):
string sql = "insert into tableA (cola, colb) values ("
+ "'" + txtBox1.Text + "',"
+ "'" + txtBox2.Text + "')";
...and then go and execute it. Depending on the contents of the text boxes you could have all sorts of things happening. Something like "');drop table tableA;--"
This does not happen when you use a DataSet as above, so that's OK
Hoewever, your code is very inefficient. The first thing you do is pull down the whole of the Address table. If this is any size it will be slow and add a lot of IO, memory, and computation to the procedure.
You are also not checking that the row to be entered is actually a new one, not a modification of an old one or a duplicate. This may or may not be important to your app, but usually is important (dup data can be a real pain). You can amend your read of the Address table to pull down e.g. a row with the same email address (or whatever is unique), and if it gets it then amend with new data as you do above.
However if the data is to be added, then you need to use parameters; Air Code again:
string sql = "insert into table (colA, colB) values (#colA, #colB)";
using (OleDbCommand com = new OleDbCommand(sql, conn))
{
com.Parameters.Add("#colA", txtBox1.Text);
com.Parameters.Add("#colB", txtBox2.Text);
com.ExecuteNonQuery();
}
(Note that different drivers have slightly different syntax on adding Parameters and I'm not sure that the OleDb command supports this syntax, but there will be something close.)
Using Parameters prevents SQL Injection, as the values of the parameters are transported not intermixed in the SQL string and so their content has no effect of the SQL eventually executed.

Related

asp.net SQL SUM clause with join on another table syntax error

I am developing a gridview fill on asp.net web forms and thought the SQL statement would have similar syntax compared to mysql.
I have a CustomerList and CustomerTracking table where CustomerTracking has a CustUUID that links to the CustomerList table entries. I am also trying to get the SUM of all entries that match the CustUUID from banner in CustomerTracking
Here is the sql statement I am trying to parse.
string command2 = "SELECT b.Name, b.Link, SUM(t.CustomerTracking) as CustomerTrackingList, SUM(t.ClickCount) as ClickCount " +
"FROM CustomerList as b JOIN CustomerTracking as t" +
"IN b.CustUUID= t.CustUUID" +
"WHERE t.date> 11/21/2006";
Have also tried explicitly stating the table names as follows.
string command2 = "SELECT CustomerList.Name, CustomerList.Link, SUM(CustomerTrackingList.CustomerTracking) CustomerTracking, SUM(CustomerTrackingList.ClickCount) ClickCount " +
"FROM CustomerList JOIN CustomerTrackingList" +
"ON CustomerList.CustUUID = CustomerTracking.CustUUID" +
"WHERE CustomerTracking.date> 11/21/2006";
I keep getting sytax errors when I try and invoke sda.fill(dt) in the code behind file below. Is the syntax correct or am I missing some decorators that asp.net is looking for?
This is the error Visual Studio is showing me!
System.Data.SqlClient.SqlException: 'Incorrect syntax near '.'.'
using (SqlCommand cmd = new SqlCommand(command2))
{
using (SqlDataAdapter sda = new SqlDataAdapter())
{
cmd.Connection = con;
sda.SelectCommand = cmd;
using (DataTable dt = new DataTable())
{
sda.Fill(dt);
GridView2.DataSource = dt;
GridView2.DataBind();
}
}
}
You don't even provide the error, however I can immediately see your date is not quoted, it should be
WHERE t.CreateDate > '11/21/2006'
and I would suggest always to use ISO dates eg '20061121'
And if you actually build your string you will be sending this to SQLServer
SELECT b.Name, b.Link, SUM(t.ImpressionCount) as ImpressionCount, SUM(t.ClickCount) as ClickCount FROM Banner as b JOIN BannerTracking as **tIN** b.BannerId = **t.BannerIdWHERE** t.CreateDate > 11/21/2006
Added -
Make sure you add white space at the end of each section to ensure it gets parsed correctly.

Windows Search SQL: Semicolon in LIKE statement causes exception (E_FAIL 0x80004005)

I am querying the windows search engine about some documents in my ASP.NET web application.
I'm looking for all documents which title contains the string "; IT" (besides other conditions, stripped from the following example).
I'm going through ADO.NET, so my code looks like this one (stripped some unimportant details):
var connString = "Provider=Search.CollatorDSO;" +
"Extended Properties='Application=Windows';";
var conn = new OleDbConnection(connString);
conn.Open();
StringWriter wSql = new StringWriter();
wSql.WriteLine("SELECT System.Title, System.Filename, System.Keywords, " +
"System.Size, System.ItemPathDisplay, System.ItemUrl, " +
"System.Search.Rank, System.DateCreated, System.DateModified " +
"FROM SYSTEMINDEX WHERE System.Title LIKE '%; IT%'");
var cmd = new OleDbCommand(wSql.ToString(), conn);
var adapter = new OleDbDataAdapter(cmd);
var result = new DataSet();
adapter.Fill(result, "doc"); // <== HERE THE RUNTIME EXPLODES
When i run this code, an exception is thrown at the last line, with code "E_FAIL 0x80004005". If i remove the semicolon from the LIKE statement, all works like a charm, but obviously i do not have the expected results, since i really really need only documents in which the title correspond to the given string.
I tried searching for reserved characters and/or escaping in Windows Search SQL, but without luck.
Any idea?
Thanks and regards,
Claudio
I found that the Windows Search API doesn't support the Like operator.

Multiple "OR" variables in SqlCommand

The database table has the following fields: UserName and FavColour. Basically, it stores the favourite colour of each user.
Instead of using concatenation, I use SqlCommand to store the information in my DataSet. It's easy when I've got 1 variable (like this):
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "SELECT * FROM Colour WHERE FavColour = #favcol";
cmd.Parameters.AddWithValue("#favcol", colourVar);
Now, I have a checkbox selection where I can choose the colour(s) for the WHERE clause. Say I've selected blue, pink, orange, and purple this time. How would I accomplish this using SqlCommand?
(Note: The number of colours selected could vary each time)
A straightforward and efficient way is to build your query.
SqlCommand cmd = new SqlCommand();
cmd.CommandText = String.Format(
"SELECT * FROM Colour WHERE FavColour IN ({0})"
, String.Join(",", MyColors.Select(c => String.Format("'{0}'", c.Replace("'", "''"))).ToArray())
);
If you want to pass multiple parameters of the same "type" to SQL Server, the "most-correct" way to do it is using table-valued parameters.
If you're not on a version of SQL Server that supports these, the next best way is to pass an XML document and use the XML functions in SQL Server to shred these back into a row-set.
The worst way you can do it is to pass a comma separated string and then have to split that back apart in SQL.
I can't decide where looping in C# and just adding as many parameters as necessary fits into the above. For a simple query such as the one shown, it may fit as the simplest option. I.e. the code would look something like:
SqlCommand cmd = new SqlCommand();
int i = 1;
var parms = new List<string>();
foreach(var colourVar = /* Obtain selected colours one at a time */)
{
var parmName = "#favcol" + i.ToString();
cmd.Parameters.AddWithValue(parmName, colourVar);
parms.Add(parmName);
}
cmd.CommandText = "SELECT * FROM Colour WHERE FavColour IN (" + string.Join(",",parms) + ")";
This wouldn't work if/when the literal query was replaced with a stored procedure, but for a small number of possible selections, it will work.

Update database row where there is a certain value

I need to update a row where there exists a certain value. The ExecuteNonQuery returns a 0 meaning the database is not being updated. When stepping through in debug, the update is hit and contains the correct values, however no updates are being done.
string verifySql = #"UPDATE UserInfo SET Verified='#Verified'
WHERE UserID='#UserID'";
using (var con = newSqlConnection(
ConfigurationManager.ConnectionStrings["UserInfoDB"].ConnectionString))
using (var cmd = new SqlCommand(verifySql, con))
{
con.Open();
cmd.Parameters.AddWithValue("#Verified", "Verified " + DateTime.Now);
cmd.Parameters.AddWithValue("#UserID", user.UserId);
Response.Write(cmd.ExecuteNonQuery());
con.Close();
}
Lose the single quotes around the parameter names in your sql statement. You don't need them, and they're making your code interpret your parameter placesholders as simple string literals.
Then remove the con.Close() line. You don't need that either; it's covered by the using block.
Finally, you might also consider changing your verified column to a simple DateTime type, rather than trying to store that data as text.

why this code enter two entries into the database

i have a code that retrieve some content and enter it the database :
MySqlConnection conn = new MySqlConnection(#"connection string");
MySqlCommand cmd = new MySqlCommand("INSERT INTO copy (id) VALUES ('" + Page.User.Identity.Name + "')", conn);
MySqlCommand cmd2 = new MySqlCommand("INSERT INTO copy (cv) VALUES ('" + mainEditor.Content.Replace("'", "''") + "')",conn);
conn.Open();
cmd.ExecuteNonQuery();
cmd2.ExecuteNonQuery();
conn.Close();
it connects and enters the data fine but it enters the data in two not one (it creates two rows instead of one)
i am using asp.net 3.5 and mysql 5.0
what am i doing wrong, thanks.
It's inserting two rows because you're executing two INSERT statements. Each time you run an INSERT it does just that: inserts a row.
I'm guessing you wanted to create a single row with both the id and cv fields populated. The SQL syntax for that is INSERT INTO copy (id, cv) VALUES ('x', 'y');
So:
MySqlCommand cmd = new MySqlCommand("INSERT INTO copy (id) VALUES ('" + Page.User.Identity.Name + "', '" + mainEditor.Content.Replace("'", "''") + "')",conn);
It's because two separate inserts are running. You can insert more than one value, try this:
MySqlCommand cmd = new MySqlCommand("INSERT INTO copy (id, cv) VALUES ('" + Page.User.Identity.Name + "', '" + mainEditor.Content.Replace("'", "''") + "')", conn);
You can comma separate the fields, and the values so it inserts into one record. Executing 2 insert commands will always create 2 records.
You didn't say which driver you're using so I'll use the documentation I found for dotConnect. I would try to use something along these lines (explanation of code below)
using( var conn = new MySqlConnection(#"connection string"))
using( cmd = new MySqlCommand("", conn) ){
cmd.CommandText = #"
INSERT INTO copy (id, cv)
VALUES (:name, :userContent)";
cmd.Parameters.Add("name", MySqlType.[correct type]]).Value = Page.User.Identity.Name;
cmd.Parameters.Add("userContent", MySqlType.[correct type], [column size]).Value = mainEditor.Content;
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
The use of the using construct is because MySqlConnection and MySqlCommand classes both implement the IDisposable interface so they need to be disposed of when you're done using them to avoid possible resource leaks.
The :name and :userContent is what I found in documentation for creating parametrized queries. This will allow the database driver to take care of escaping all of the special characters out of user input to avoid SQL injection attacks. This part is actually really important, there are some REALLY sophisticated SQL injection attacks out there, so there's a good chance simply escaping ' (as you were doing) isn't enough.

Resources