Preventing SQL Injection in ASP.Net - 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.

Related

insert multiple rows with one query using ms access db

I am trying to get multiple rows into a table hence my attempt to get the row number and put it into a for loop, the countC is exactly the same number of rows as the select statement, so the issue is not there
I'm using an oledb connection as my code is in vb asp.net but my database is in ms access 2003
For c As Integer = 1 To countC
Dim cmdstring As String
cmdstring = " INSERT INTO [KN - ProductionMachineAllocation] (BatchNo, ComponentID)
SELECT POH.BatchNo, SSCDD.ComponentID
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY BatchNo ASC) AS rownumber
([KN - ProductionOrderHeader] AS POH
INNER JOIN [FG - End Product Codes] AS EPC
ON POH.ProductID = EPC.ProductID)
INNER JOIN ([KN - ProductionOrderDetails] AS POD
INNER JOIN [FG - Style Size Comp Def Details] AS SSCDD
ON POD.SizeID = SSCDD.SizeID)
ON (POH.BatchNo = POD.BatchNo)
AND (EPC.StyleID = SSCDD.StyleID)
WHERE POH.BatchNo = '" & BatchNo & "'
) AS temptablename
WHERE rownumber IN (" & c & ");"
Dim con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\Shantara Production IT.mdb")
Dim cmd As New OleDbCommand(cmdstring)
cmd.CommandType = CommandType.Text
cmd.Connection = con
cmd.Connection.Open()
cmd.ExecuteNonQuery()
cmd.Connection.Close()
Next
I found out that ms access doesn't support ROW_NUMBER() so I need to find another going through each row since ms access doesn't support multi row insert by insert into select statement such as mine, any suggestions around my problem?
Most databases are able to do all this work much more efficiently entirely in the database. Certainly in SQL Server I could get entire thing down to a single query. Access is a little different, since vbscript is its procedural language, rather than something more like t-sql. There's still probably a way to do it, but since what you have works, we can at least focus on making that better.
GridViews are visual constructs that will use up extra memory and resources. If Access won't do a real INSERT/SELECT, you can at least read direct from the previous result set into your insert. You can also improve on this significantly by using parameters and re-using a single open connection for all the inserts:
Dim cnString As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\Shantara Production IT.mdb"
Dim SQLDown As String = _
"SELECT DISTINCT POH.BatchNo, SSCDD.ComponentID
FROM ([KN - ProductionOrderHeader] AS POH
INNER Join [FG - End Product Codes] AS EPC
On POH.ProductID = EPC.ProductID)
INNER Join([KN - ProductionOrderDetails] AS POD
INNER Join [FG - Style Size Comp Def Details] AS SSCDD
On POD.SizeID = SSCDD.SizeID)
On (POH.BatchNo = POD.BatchNo)
And (EPC.StyleID = SSCDD.StyleID)
WHERE POH.BatchNo = ? "
Dim SQLUp As String = _
" INSERT INTO [KN - ProductionMachineAllocation]
(BatchNo, ComponentID)
VALUES( ?, ? )"
Dim dt As New DataTable
Using con As New OleDbConnection(cnString), _
cmd As New OleDbCommand(SQLDown, con)
'Guessing at parameter type/length here.
'Use the actual column type and size from your DB
cmd.Parameters.Add("#BatchNo", OleDbType.VarWChar, 10).Value = BatchNo
con.Open()
dt.Load(cmd.ExecuteReader())
End Using
Using con As New OleDbConnection(cnString), _
cmd As New OleDbCommand(SqlUp, con)
'Guessing at parameter types/lengths again
cmd.Parameters.Add("#BatchNo", OleDbType.VarWChar, 10)
cmd.Parameters.Add("#ComponentID", OleDbType.Integer)
'Connection is managed *outside of the loop*. Only one object created, only one negotiation with DB
con.Open()
For Each row As DataRow In dt.Rows
cmd.Parameters(0).Value = row(0)
cmd.Parameters(1).Value = row(1)
cmd.ExecuteNonQuery()
Next
End Using
Normally, with any ADO.Net provider you do not re-use your connection or command objects. You want a new connection object for every query sent to the DB to allow connection pooling to work correctly. Using the connection in a tight loop like this for the same query is one of the few exceptions.
I might be able to improve further by sticking with the active DataReader, rather than first loading it into a DataTable. That would allow us to avoid loading the entire result set into memory. You would only ever need one record in memory at a time. Certainly this would work for Sql Server. However, Access was designed mainly as a single-user database. It doesn't really like multiple active connections at once, and I'm not sure how it would respond.
It would also be nice to be able to do all of this work in a transactional way, where there's never any risk of it failing part way through the loop and getting stuck with half the updates. Sql Server would handle this via a single INSERT/SELECT query or with an explicit transaction. But, again, this isn't the kind of the Access is designed for. It probably does have a way to do this, but I'm not familiar with it.
OK SO I finally found a way around it, it's a bit of a long process but basically I loaded the SELECT statement(with multiple rows) into a gridview table and the used a for loop to insert it into my insert into statement. bellow is my code:
Displaying into a table
Dim Adapter As New OleDbDataAdapter
Dim Data As New DataTable
Dim SQL As String
Dim con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\Shantara Production IT.mdb")
Dim cmd As New OleDbCommand()
grdvmachincomp.Visible = false
SQL = "SELECT DISTINCT POH.BatchNo, SSCDD.ComponentID
FROM ([KN - ProductionOrderHeader] AS POH
INNER Join [FG - End Product Codes] AS EPC
On POH.ProductID = EPC.ProductID)
INNER Join([KN - ProductionOrderDetails] AS POD
INNER Join [FG - Style Size Comp Def Details] AS SSCDD
On POD.SizeID = SSCDD.SizeID)
On (POH.BatchNo = POD.BatchNo)
And (EPC.StyleID = SSCDD.StyleID)
WHERE POH.BatchNo = '" & BatchNo & "'"
con.Open()
cmd.Connection = con
cmd.CommandText = SQL
Adapter.SelectCommand = cmd
Adapter.Fill(Data)
grdvmachincomp.DataSource = Data
grdvmachincomp.DataBind()
cmd.Connection.Close()
Insert into through for loop
For c As Integer = 0 To grdvmachincomp.Rows.Count - 1
Dim cmdstring As String
cmdstring = " INSERT INTO [KN - ProductionMachineAllocation] (BatchNo, ComponentID) VALUES('" & grdvmachincomp.Rows(c).Cells(0).Text & "', " & grdvmachincomp.Rows(c).Cells(1).Text & ");"
Dim con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\Shantara Production IT.mdb")
Dim cmd As New OleDbCommand(cmdstring)
cmd.CommandType = CommandType.Text
cmd.Connection = con
cmd.Connection.Open()
cmd.ExecuteNonQuery()
cmd.Connection.Close()
Next

VB.NET ORA-01745: invalid host/bind variable name

For the last 2 hours I was trying figure out why the parameter could not be bound (Well I know I was not using the "using" block. And I know System.Data.OracleClient is deprecated.) Please help me see what's wrong with the following code:
Dim nCount As Integer
sSQL = " SELECT COUNT(*) FROM USERS WHERE USER_ID = :UID "
Dim conn As OracleConnection = New OracleConnection(ConfigurationSettings.AppSettings("connString"))
conn.Open()
Dim cmd As OracleCommand = New OracleCommand(sSQL, conn)
cmd.CommandType = CommandType.Text
With cmd
.Parameters.Add(New OracleParameter(":UID", txtUserID.Text))
End With
Try
nCount = cmd.ExecuteScalar()
Catch ex As Exception
End Try
I have tried all variations I can find online: with or without colon in the Parameters.Add, Add or AddWithValue, Add in a parenthesis or create a new OracleParameter object then add it...Nothing seems to work.
But if I just hard-code the USER_ID in the query, remove the parameter.Add, it would return a value.
A HA!
UID is actually a reserved word in Oracle. Change your UID variable to something that is not a reserved word.
For me it seems that you missed something, while experimenting with different combinations.
This variant must work:
Dim nCount As Integer
sSQL = "SELECT COUNT(*) FROM USERS WHERE USER_ID = :UID"
Dim conn As OracleConnection = New OracleConnection(ConfigurationSettings.AppSettings("connString"))
conn.Open()
Dim cmd As OracleCommand = New OracleCommand(sSQL, conn)
cmd.CommandType = CommandType.Text
cmd.Parameters.Add("UID", OracleType.VarChar).Value = txtUserID.Text
nCount = cmd.ExecuteScalar()
Please try it ...
Do yourself a favor and at least look into ODP from Oracle. You'll need it with Microsoft finally pulls the plus on its OracleClient. The switch over to ODP is very easy.
In your situation, I'd leave off the parameter name. You're binding by position anyway.
The SQL syntax is also a little different in the Microsoft implementation. Use a ? to act as each placeholder. See http://msdn.microsoft.com/en-us/library/system.data.oracleclient.oracleparameter.aspx for further information.

A nested SQL query within a while loop in ASP.NET

I intended to do another SQL query inside here and retrieve data from another table by using the "category_id"
I know the problems that asp.net required me to close the data reader before proceed to another query. But is there any solution for me to do another query and open another data reader within the opening data reader?
My current code is as follows...
Dim dr, dr2 As SqlDataReader
Dim conn As SqlConnection
Dim cmd, cmd2 As SqlCommand
conn = New SqlConnection("server=XXX-PC;user=sa;password=abc123321;database=xxx")
cmd = New SqlCommand("SELECT * FROM category ORDER BY category_Name", conn)
conn.Open()
dr = cmd.ExecuteReader()
Do While dr.Read()
Dim category_id As Integer = dr.GetInt32(0)
Dim category_name As String = dr.GetString(1)
/* Another data reader and query here */
Loop
dr.Close()
conn.Close()
You have a few options:
Create a new connection to your database, and create the second data reader from that.
Use a SqlDataAdapter, and dump your queries into in-memory DataTables, and loop through them.
Use an Object-Relational mapper, like NHibernate, or Entity Framework, and obviate all these problems completely.
2 would probably be the simplest, and quickest to implement. 3 will require a bit of a learning curve, but would likely be worth it in the long run. 1 is actually a terrible idea; don't do it. I probably shouldn't even have listed it.
You can use MARS the ultimate feature of vs2005
[Multiple Active Result Sets ]
instead of datareader use Idatareader
Dim dr, dr2 As IDataReader
Dim conn As SqlConnection
Dim cmd, cmd2 As SqlCommand
conn = New SqlConnection("server=XXX-PC;user=sa;password=abc123321;database=xxx")
cmd = New SqlCommand("SELECT * FROM category ORDER BY category_Name", conn)
conn.Open()
dr = cmd.ExecuteReader()
Do While dr.Read()
Dim category_id As Integer = dr.GetInt32(0)
Dim category_name As String = dr.GetString(1)
/* Another data reader and query here */
cmd2.CommandText=” your query”
dr2 = cmd2.ExecuteReader();
Loop
dr2.Close();
dr.Close()
conn.Close()
MARS is disabled by default on the Connection object. You have to enable it with the addition of MultipleActiveResultSets=true in your connection string.
Create a Separate function, and create private data adapter & data set into it and perform your logic then return value to main procedure.

how to run two queries using the code snippet below?

How to Run two Update Sql Queries using this Sql Snippet ?
The code mentioned below is updating values only in one table .... i want to update data in two different tables using the code mentioned below :
can anybody reedit this code ?
Try
Using conn = New SqlConnection(constr)
Using cmd = conn.CreateCommand()
conn.Open()
Dim sql As String =
"UPDATE a1_ticket
SET Ticket_no =#ticketNo,
BANK = #bank,
PAID = #paid,
BID = #bid
WHERE ITC = #ticketNo"
cmd.CommandText = sql
cmd.Parameters.AddWithValue("#bank", Literal20.Text)
cmd.Parameters.AddWithValue("#paid", Label1.Text)
cmd.Parameters.AddWithValue("#bid", Literal21.Text)
cmd.Parameters.AddWithValue("#ticketNo", Literal3.Text)
cmd.ExecuteNonQuery()
End Using
End Using
Catch ex As Exception
Response.Write(ex.Message)
End Try
Create a Stored Procedure that updates the two tables and execute it using a StoredProcedure Command...
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "UpdateTheTwoTables";
....
Modify the SQL statement to update the two tables.
Using a Stored Procedure is the cleanest way code wise. If you don't feel comfortable doing it like that, I'm sure you can do it like this:
Try
Using conn = New SqlConnection(constr)
Using cmd = conn.CreateCommand()
conn.Open()
Dim sql As String = "UPDATE a1_ticket SET Ticket_no =#ticketNo, BANK = #bank, PAID = #paid, BID = #bid WHERE ITC = #ticketNo"
cmd.CommandText = sql
cmd.Parameters.AddWithValue("#bank", Literal20.Text)
cmd.Parameters.AddWithValue("#paid", Label1.Text)
cmd.Parameters.AddWithValue("#bid", Literal21.Text)
cmd.Parameters.AddWithValue("#ticketNo", Literal3.Text)
cmd.ExecuteNonQuery()
End Using
//
Using cmd = conn.CreateCommand()
conn.Open()
Dim sql As String = "UPDATE a2_ticket SET Ticket_no =#ticketNo, BANK = #bank, PAID = #paid, BID = #bid WHERE ITC = #ticketNo"
cmd.CommandText = sql
cmd.Parameters.AddWithValue("#bank", Literal20.Text)
cmd.Parameters.AddWithValue("#paid", Label1.Text)
cmd.Parameters.AddWithValue("#bid", Literal21.Text)
cmd.Parameters.AddWithValue("#ticketNo", Literal3.Text)
cmd.ExecuteNonQuery()
End Using
End Using
Catch ex As Exception
Response.Write(ex.Message)
End Try
It's a sketch of what I'm trying to say, you may want to change a few things here and there, but the point is you can just update your two tables one after the other. It's not possible in one update statement afaik.
you can also use
Dim sql As String = # "Query for first update;
Query for second update;";
Well as you havent said anything about the second table, or the data you're sending it. I havent put this through the compiler to verify it, but the concept I'd suggest would be
You could do:
void UpdateDB(String sql, String[][] params)
{
Try
{
SqlConnection conn = New SqlConnection(constr);
SqlCommand cmd = conn.CreateCommand();
conn.Open();
cmd.CommandText = sql;
for(int i=0; i<params.length; i++)
{
cmd.Parameters.AddWithValue(params[i,0] params[i,1]);
}
cmd.ExecuteNonQuery();
}
Catch (Exception ex)
{
Response.Write(ex.Message);
}
}
eg send the SQL and the parameters to the function and have it do all the work..

Parameterized query - error when executing a query

I've recently been trying to figure out a way to get parameterized queries to work and i think i'm almost there but seem to be getting an error when executing my query
Here's the code
Dim LogData2 As sterm.MarkData = New sterm.MarkData()
Dim query As String = ("Select * from openquery (db, 'SELECT * FROM table WHERE investor=#investor')")
Dim cmd As New SqlCommand(query)
cmd.Parameters.AddWithValue("#investor", 34)
Dim drCode2a As DataSet = LogData2.StermQ3(query)
I've debugged it and it doesn't seem to be putting the parameter into the query.
"Select * from openquery (db, 'SELECT * FROM table WHERE investor=#investor')"
That's what i get when i debug the line Dim drCode2a As DataSet = LogData2.StermQ3(query)
Any ideas what i'm doing wrong?
SOLUTION
Here's 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()
Thanks for the help
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

Resources