Avoiding SQL injection when saving user-agent using ASP.net - asp.net

I'm saving the browser user-agent of my users for stats purposes.
As you already know, user-agent can be modified. I would like to know if I should do anything to protect against SQL Injection.
I'm using Stored Procedures for inserting.
Many thanks.

Use parameters with stored procedures or use parameters with dynamic SQL.
Here's the example from MSDN:
SqlDataAdapter dataAdapter = new SqlDataAdapter(
"SELECT CustomerID INTO #Temp1 FROM Customers " +
"WHERE CustomerID > #custIDParm; SELECT CompanyName FROM Customers " +
"WHERE Country = #countryParm and CustomerID IN " +
"(SELECT CustomerID FROM #Temp1);",
connection);
SqlParameter custIDParm = dataAdapter.SelectCommand.Parameters.Add(
"#custIDParm", SqlDbType.NChar, 5);
custIDParm.Value = customerID.Text;
SqlParameter countryParm = dataAdapter.SelectCommand.Parameters.Add(
"#countryParm", SqlDbType.NVarChar, 15);
countryParm.Value = country.Text;
connection.Open();
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet);

Use a prepared statement. Make sure you use a prepared statement for all SQL operations, even if the data comes out of the database.

Related

Query SQL Server using session variable

I'm trying to query a SQL Server database table based on a user variable (using ASP.NET and C#). I want to be able to pull just the user's unique records from the Waste Application Information table where the Farm Owner name is equal to the variable name (which is a string).
Here's part of my code:
conn.Open();
WasteAppData = "SELECT * FROM [WASTE APPLICATION INFORMATION] WHERE [FARM OWNER] = (user variable) ";
SqlCommand com = new SqlCommand(WasteAppData, conn);
GridView1.DataSource = com.ExecuteReader();
GridView1.DataBind();
If I replace the "(user variable)" with the actual value in the table column it does work correctly. Like this: 'Joe Smith' I've tried referencing the variable which is pulled from another webform with no luck... I think my syntax is incorrect? Any help would be great!
You need to do it this way:
WasteAppData = "SELECT * FROM [WASTE APPLICATION INFORMATION] WHERE [FARM OWNER] = #FarmOwn";
using (SqlCommand cmdSQL = new SqlCommand(WasteAppData , conn)
{
cmdSQL.Parameters.Add("#FarmOwn", SqlDbType.NVarChar).Value = strFarmOwnwer;
cmdSQL.Connection.Open();
GridView1.DataSource = cmdSQL.ExecuteReader;
GridView1.DataBind();
}
In this case "strFarmOwner" would be replaced with your actual variable that holds the value you want.

OLEDB query to SQL Server fails

I have two SQL queries:
A.
SELECT (upper(rtrim(ltrim(lastname))) + upper(rtrim(ltrim(firstname))) +
upper(rtrim(ltrim(middlename))) + rtrim(ltrim(v)) ) AS userCompareStr
FROM atable ;
and
B.
SELECT (upper(rtrim(ltrim(lastname))) + upper(rtrim(ltrim(firstname))) +
upper(rtrim(ltrim(middlename))) + rtrim(ltrim(v)) ) AS userCompareStr
FROM atable WHERE userCompareStr='GAPYLE1111' ;
I have the following code:
Dim sql As String
Dim conn As OleDbConnection
Dim cmd As OleDbDataAdapter
Dim ds As DataSet
Dim tbl As DataTable
conn = " something here "
cmd = New OleDbDataAdapter(sql, conn)
ds = New DataSet
cmd.Fill(ds)
tbl = New DataTable
tbl = ds.Tables(0)
Near as I can tell it seems to work when sql is set to string A, but not when it's set to string B.
This leads me to suspect that there is something wrong with the clause
WHERE userCompareStr='GAPYLE1111'
Can I not use the alias userCompareStr in this way? I can't find any examples of this kind of use, but I do find analogous use when alias is used for table name -- and I don't see anything against that kind of us.
You have three options.
1) repeat what you did in the select in the where
SELECT (upper(rtrim(ltrim(lastname))) + upper(rtrim(ltrim(firstname))) +
upper(rtrim(ltrim(middlename))) + rtrim(ltrim(v)) ) AS userCompareStr
FROM atable
WHERE (upper(rtrim(ltrim(lastname))) + upper(rtrim(ltrim(firstname))) +
upper(rtrim(ltrim(middlename))) + rtrim(ltrim(v)) ) ='GAPYLE1111' ;
2) Use a common table expression
with CTE AS
(SELECT (upper(rtrim(ltrim(lastname))) + upper(rtrim(ltrim(firstname))) +
upper(rtrim(ltrim(middlename))) + rtrim(ltrim(v)) ) AS userCompareStr
FROM atable )
SELECT userCompareStr FROM CTE where userCompareStr = 'GAPYLE1111';
3) Inline query see Maziar Taheri's answer
As an aside I hope 'GAPYLE1111' doesn't come from user input, otherwise you're exposing yourself to SQL Injection attacks. Use parameterized queries instead
No, you cannot use an aliased column in the WHERE clause.
See Using an Alias column in the where clause in ms-sql 2000
(the article is about SQL 2000, but it still applies today)
you cannot use an alias you have set in the select clause, inside the where clause.
try this:
SELECT * FROM
(
SELECT (upper(rtrim(ltrim(lastname))) + upper(rtrim(ltrim(firstname))) + upper(rtrim(ltrim(middlename))) + rtrim(ltrim(v)) ) AS userCompareStr FROM atable)
as nested
WHERE userCompareStr='GAPYLE1111' ;
I stepped away from the problem for a while, worked on something else, and came back to it.
I have solved the primary problem by switching from using oledb to "something else."
I'm not sure what the new (to me) method is called - except maybe "native sqlserver?"
Important points:
Cannot use field name alias in the WHERE clause. (as per maziar and matt)
Conrad's fix #1 worked on OLEDB, but I don't like that method because it's verbose (and the real command is a lot more complicated than the scaled down example I provide here) and there is a LOT of different invocations. Error-prone and hard to read (but works in a pinch).
To get either the WITH or the nested select work I had to switch from OLEDB to "native sqlserver" (or whatever it's called). The WITH (as suggested by Conrad) is my preferred solution - much easier to read. The nested select (suggested by Maziar) also works when I switch from OLEDB to native.
I need to switch to "parameterized queries" to avoid sql injection attacks as noted by Conrad.
Anyway, suggestions above work when I switched to that method.
Instead of using
Provider=SQLOLEDB
I used:
providerName="System.Data.SqlClient"
I now make no reference to oledb (such as oledbadapter), but instead make references to sqlDataAdapter. I ignore the upper, ltrim, and trim functions (because it turns out they weren't the issue) and focus on the WITH which is what oledb had been choking on. Here's what I got to work:
Dim conn As New SqlConnection("server=localhost;database=DB;Integrated Security=SSPI;")
Dim sql As String
Dim da As SqlDataAdapter
Dim ds As DataSet = New DataSet()
Dim tbl As DataTable = New DataTable
conn = New SqlConnection()
conn.ConnectionString = ConfigurationManager.ConnectionStrings("DB").ConnectionString
Sql = " WITH cte AS "
sql = sql & "(lastname + firstname + middlename"
Sql = Sql & " + v) as userCompareStr FROM atable ) "
sql = sql & "SELECT userCompareStr FROM cte WHERE userCompareStr = '" & "GAPYLE1111" & "' ;"
da = New SqlDataAdapter(sql, conn)
da.Fill(ds)
tbl = ds.Tables(0)
TextBox2.Text = sql
If tbl.Rows.Count < 1 Then
TextBox1.Text = "no items"
Else
TextBox1.Text = tbl.Rows.Count & " items selected"
End If
conn.Dispose()
Also, in web.config, I added:
I have not added the SQL Injection fix stuff yet, but I am looking into it and I'm sure now that this is something I need to do. I found some information on that here:
http://msdn.microsoft.com/en-us/library/ff648339.aspx
and here:
http://weblogs.asp.net/cibrax/archive/2006/09/28/Parameterized-Queries-_2800_Oracle_2C00_-SQLServer_2C00_-OleDb_2900_.aspx
but I need some time to experiment with it. Thanks for the help and the pointers.

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.

direct SQL execution to get count using Enterprise library

I am new to asp.net development and using enterprise library in my application in the following way.
Database db = DatabaseFactory.CreateDatabase();
DbCommand cmd = db.GetStoredProcCommand("sp_MakePayment");
db.AddInParameter(cmd, "#BillGenID", System.Data.DbType.Int32);
db.SetParameterValue(cmd, "#BillGenID", billgenID);
db.AddInParameter(cmd, "#PayDate", System.Data.DbType.String, 50);
db.SetParameterValue(cmd, "#PayDate", mypaydate.Text);
db.AddInParameter(cmd, "#TransNo", System.Data.DbType.String, 50);
db.SetParameterValue(cmd, "#TransNo", transno.Text);
db.AddInParameter(cmd, "#AmtToPay", System.Data.DbType.Double);
db.SetParameterValue(cmd, "#AmtToPay", Convert.ToDouble(paidamount.Text));
////Execute Stored Procedure
int i = 0;
i = db.ExecuteNonQuery(cmd);
Now I am in a situation where i need to run this inlnie query using the same method to get the count of records and read in a variable. for example following query to find existing bill.
string bill_id = "1234";
string dofpayment = "11/03/2011";
mysql = "Select count(*) from payments where bill_id = " + bill_id + " and payment_date = " + dofpayment ;
Now how to incorporate the above lines using the enterprise library block.
thanks
If you enterprise library supports text commands alongside with stored procedures, then you should write a code which creates a text command and then passes your bill_id and dofpayment as parameters to it.
Otherwise, you need to create a stored procedure with these two parameters and then call that in similar way shown in your code example for stored procedure.
I hope this helps!
string sSql = "SELECT CustomerID, CompanyName, City, Country" + " FROM Customers WHERE Country = #sCountry"; Database db = DatabaseFactory.CreateDatabase(); DBCommandWrapper cmd = db.GetSqlStringCommandWrapper(sSql); cmd.AddInParameter("#sCountry", DbType.String, sCountry); DataSet ds = new DataSet(); db.LoadDataSet(cmd, ds, "Customers");
please read the msdn reference

Preventing SQL Injection in ASP.Net

I have this code
UPDATE OPENQUERY (db,'SELECT * FROM table WHERE ref = ''"+ Ref +"'' AND bookno = ''"+ Session("number") +"'' ')
How would I prevent SQL Injections on this?
UPDATE
Here's what i'm trying
SqlCommand cmd = new SqlCommand("Select * from Table where ref=#ref", con);
cmd.Parameters.AddWithValue("#ref", 34);
For some reason everything I try and add it doesn't seem to work I keep getting SQL Command mentioned below.
The error is this
'SqlCommand' is a type and cannot be used as an expression
I'm taking over someone else's work so this is all new to me and I would like do things the right way so if anyone can provide any more help on how to make my query above safe from SQL injections then please do.
UPDATE NO 2
I added in the code as VasilP said like this
Dim dbQuery As [String] = "SELECT * FROM table WHERE ref = '" & Tools.SQLSafeString(Ref) & "' AND bookno = '" & Tools.SQLSafeString(Session("number")) & "'"
But I get an error Tools is not declared do I need to specify a certain namespace for it to work?
UPDATE
Has anyone got any ideas on the best of getting my query safe from SQL injection without the errors that i'm experiencing?
UPDATE
I now have it so it work without the parameters bit here's my updated source code any idea why it won't add the parameter value?
Dim conn As SqlConnection = New SqlConnection("server='server1'; user id='w'; password='w'; database='w'; pooling='false'")
conn.Open()
Dim query As New SqlCommand("Select * from openquery (db, 'Select * from table where investor = #investor ') ", conn)
query.Parameters.AddWithValue("#investor", 69836)
dgBookings.DataSource = query.ExecuteReader
dgBookings.DataBind()
It works like this
Dim conn As SqlConnection = New SqlConnection("server='server1'; user id='w'; password='w'; database='w'; pooling='false'")
conn.Open()
Dim query As New SqlCommand("Select * from openquery (db, 'Select * from table where investor = 69836') ", conn)
dgBookings.DataSource = query.ExecuteReader
dgBookings.DataBind()
The error i'm getting is this
An error occurred while preparing a query for execution against OLE DB provider 'MSDASQL'.
And it's because it isn't replacing the #investor with the 69836
Any ideas?
SOLUTION
Here is how I solved my problem
Dim conn As SqlConnection = New SqlConnection("server='h'; user id='w'; password='w'; database='w'; pooling='false'")
conn.Open()
Dim query As New SqlCommand("DECLARE #investor varchar(10), #sql varchar(1000) Select #investor = 69836 select #sql = 'SELECT * FROM OPENQUERY(db,''SELECT * FROM table WHERE investor = ''''' + #investor + ''''''')' EXEC(#sql)", conn)
dgBookings.DataSource = query.ExecuteReader
dgBookings.DataBind()
Now I can write queries without the worry of SQL injection
Try using a parameterized query here is a link http://www.aspnet101.com/2007/03/parameterized-queries-in-asp-net/
Also, do not use OpenQuery... use the this to run the select
SELECT * FROM db...table WHERE ref = #ref AND bookno = #bookno
More articles describing some of your options:
http://support.microsoft.com/kb/314520
What is the T-SQL syntax to connect to another SQL Server?
Edited
Note: Your original question was asking about distributed queries and Linked servers. This new statement does not reference a distributed query. I can only assume you are directly connecting to the database now. Here is an example that should work.
Here is another reference site for using SqlCommand.Parameters
SqlCommand cmd = new SqlCommand("Select * from Table where ref=#ref", con);
cmd.Parameters.Add("#ref", SqlDbType.Int);
cmd.Parameters["#ref"] = 34;
Edited:
Ok Jamie taylor I will try to answer your question again.
You are using OpenQuery becuase you are probably using a linked DB
Basically the problem is the OpenQuery Method takes a string you cannot pass a variable as part of the string you sent to OpenQuery.
You can format your query like this instead. The notation follows servername.databasename.schemaname.tablename. If you are using a linked server via odbc then omit databasename and schemaname, as illustrated below
Dim conn As SqlConnection = New SqlConnection("your SQL Connection String")
Dim cmd As SqlCommand = conn.CreateCommand()
cmd.CommandText = "Select * db...table where investor = #investor"
Dim parameter As SqlParameter = cmd.CreateParameter()
parameter.DbType = SqlDbType.Int
parameter.ParameterName = "#investor"
parameter.Direction = ParameterDirection.Input
parameter.Value = 34
Use parameters instead of concatenating your SQL query.
Assuming your database engine being SQL Server, here's a piece of code which I hope will help.
Using connection As SqlConnection = new SqlConnection("connectionString")
connection.Open()
Using command As SqlCommand = connection.CreateCommand()
string sqlStatement = "select * from table where ref = #ref and bookno = #bookno";
command.CommandText = sqlStatement
command.CommandType = CommandType.Text
Dim refParam As SqlDataParameter = command.CreateParameter()
refParam.Direction = ParameterDirection.Input
refParam.Name = "#ref"
refParam.Value = Ref
Dim booknoParam As SqlDataParameter = command.CreateParameter()
booknoParam.Direction = ParameterDirection.Input
booknoParam.Name = "#bookno"
booknoParam.Value = Session("number")
Try
Dim reader As SqlDataReader = command.ExecuteQuery()
' Do your reading job here...'
Finally
command.Dispose()
connection.Dispose()
End Try
End Using
End Using
To sum it all up, avoid SQL statement concatenation at all cost, and use parameterized quesries!
Here is an interesting link that brings you through SQL injection problem resolution on MSDN:
How To: Protect From SQL Injection in ASP.NET
use sqlparameters like:
SqlCommand cmd = new SqlCommand("Select * from Table where id=#id", con);
cmd.Parameters.AddWithValue("#id", 34);
you can use parameterized queries.
http://www.functionx.com/aspnet/sqlserver/parameterized.htm
SqlCommand cmd = new SqlCommand("Select * from Table where ref=#ref", con);
cmd.Parameters.AddWithValue("#ref", 34);
it does not work because it is written in C#, not VB.
Try something like
Dim cmd As New SqlCommand("Select * from Table where ref=#ref", con)
cmd.Parameters.AddWithValue("ref", 34)
My preferred way is to let Visual Studio handle it all by creating a DAL:
http://www.asp.net/data-access/tutorials/creating-a-data-access-layer-cs
Use LINQ. It parametrizes queries automatically.
Check out ORM as an alternative (very good way to go if you are building something medium-sized or big). It takes a little time to configure it, but then development becomes VERY fast. You choose from the native, Linq to SQL or Entity Framework, OR, try any other ORM which works with .NET.

Resources