How to override SqlCommand to grab generated TSQL - c#

Is there any way to intercept the SQL that's generated by SqlCommand?
I currently have a method that will execute a stored procedure:
public int ExecSP(string spName, params objec[] params)
{
using (SqlConnection con = new SqlConnection("AdventureWorks"))
using (SqlCommand cmd = new SqlCommand(spName, con))
{
cmd.CommandType = CommandType.StoredProcedure;
//..calls method to add params and values to cmd object
con.Open();
return cmd.ExecuteNonQuery();
}
}
When I use this to call the following:
ExecSP("HumanResources.uspUpdateEmployeePersonalInfo", 1, "295847284", new DateTime(1963, 3, 2), "S", "M");`
I get the following in SQLProfiler:
exec HumanResources.uspUpdateEmployeePersonalInfo #BusinessEntityID=1,#NationalIDNumber=N'295847284',#BirthDate='1963-03-02 00:00:00',#MaritalStatus=N'S',#Gender=N'M'
What I would like to do is intercept that SQL command so that I may add a comment to the end of it that will contain some pertinent information so that it looks like:
exec HumanResources.uspUpdateEmployeePersonalInfo #BusinessEntityID=1,#NationalIDNumber=N'295847284',#BirthDate='1963-03-02 00:00:00',#MaritalStatus=N'S',#Gender=N'M' -- IMPORTANT INFORMATION HERE
I can't change the CommandType to Text and I can't add an extra parameter to the stored procedure. I tried looking at these other questions but had no luck:
Can I override SqlCommand functions?
SqlCommand to T-SQL

In the case of CommandType = CommandType.StoredProcedure, this isn't possible.
Why? The SQL you see in SQL Profiler isn't generated by the client. :-/
When SqlCommand executes a CommandType.StoredProcedure command, it sends SQL Server an execute remote procedure call message with the name of the stored procedure and a data structure containing the parameters.
The TextData SQL Profiler displays for the RPC:Starting/RPC:Completed events is generated server-side. Since this text isn't generated client-side, it's not possible to modify it client-side.

What you're seeing is a debug/profile message, but that doesn't perfectly represent what was actually executed (the database will use sp_executesql() behind the scenes).
What you are looking for doesn't exist. The whole point of using parameter objects is that parameter values are NEVER, at any time, included as part of the sql command string, even on the server.
This accomplishes three main things:
It prevents any possibility of sql injection attacks via that query. You'll never have a problem with some weird unicode character set problem allowing an attacker to insert a command into the data for your query, like they did a couple years back with php/mysql, or some new language feature creating a situation where your old escape code wasn't good enough. The user-data portion of a query is always separated from the command logic portion of the query at every stage.
It improves performance, by allowing the server to cache the execution plan. This saves compile steps as your application executes what may be the same query over and over, just with different parameter data.
It avoids problems with formatting for things like dates, text data with apostrophes, and the like.
If you want to get debugging data, write a method to output the query and the parameter data that goes with it. If you want to add info the your profile trace, you can switch to CommandType.Text and call the stored procedure via your own EXEC procedurename #param1, #param2, ... #paramN -- INFO HERE string.

Related

How to generate a SQL Command string that is safe from SQL Injection?

I have a situation where I have to take input from a user, create a SQL command and send that command to a service that will execute the SQL. The service ONLY allows for a SQL string -- not additional parameters; so I am forced to create the entire SQL statement on my end of things.
I do not have any kind of access to the database itself -- only a service that sits overtop of it.
I realize the following is NOT safe:
var sql = $"SELECT * FROM tablename WHERE name = '{incomingdata.searchName}'";
But if I generate SQL with parameters, would this be safe from SQL injection?
var sql = $#"
DECLARE #Name varchar(50);
SET #Name = '{incomingdata.searchName}';
SELECT * FROM tablename WHERE name = #Name";
This is not an ideal situation. I would try and look for a parametrized way to solve this problem failing that I would test the input and in ANY case where a test fails not allow the query at all and ask the user to re-enter.
Do the following tests:
Length of input is smaller than a max name size (25 characters?)
All input characters are in the alphabet
No reserved SQL words (easy to find with a google search)
If the input does not fail any of these tests you should be OK. DON'T try to sanitize the input -- this can be hard/impossible to do with international character sets.
Disclosure: My background is C++, Java and TypeScript/JavaScript, not C#
Would this be more appropriate:
SqlCommand sql = new SqlCommand("SELECT * FROM tablename WHERE name = #Name");
sql.Parameters.Add("#Name", SqlDbType.VarChar, 50).Value = incomingdata.searchName);
Sanitizing the user data before it gets to this stage, using a trusted package, might also be helpful.
The second example is better, but still not fully secure.
SQL injection attacks can still occur if the input data is maliciously crafted, regardless of whether the input data is directly embedded into the string or used as a parameter.
A more secure way to handle this situation is to use parameterized queries, which automatically escape any special characters in the input data and prevent SQL injection attacks.
Unfortunately, if the service you are using only accepts raw SQL strings and does not support parameters, your options are limited. In this case, it is recommended to validate and sanitize the input data before constructing the SQL string to minimize the risk of SQL injection.
To make the SQL statement safe from SQL injection, you can use parameterized queries. In .NET, you can use the SqlCommand.Parameters property to define parameters and their values. The following is an example:
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string sql = "SELECT * FROM tablename WHERE name = #Name";
using (SqlCommand command = new SqlCommand(sql, connection))
{
command.Parameters.AddWithValue("#Name", incomingdata.searchName);
using (SqlDataReader reader = command.ExecuteReader())
{
// process the results
}
}
}
In this example, the value of incomingdata.searchName is passed as a separate parameter and not directly concatenated into the SQL string. This protects against SQL injection because the parameter value is automatically escaped and properly formatted by the .NET framework.
You are along the right lines. You never want to use a variable that can be changed. Instead, you need to use SQL parameters. You can add a SQL parameter like this:
command.Parameters.Add(new SqlParameter("#Name", "incomingdata.searchName"));
And then refer to it in your query like this:
SELECT *
FROM tablename
WHERE name = #Name";
Once this is done, when a user tries to change the value of a variable the query will return no results. It should be said that this way of doing it does result in the SQL property assuming the type of the C# variable. There are other ways of doing this if you want to specify the type of the property to be different from the variables type. This is a useful resource https://jonathancrozier.com/blog/preventing-sql-injection-in-c-sharp-applications

What is purpose of using OleDb Parameters? [duplicate]

This question already has answers here:
When should I use prepared statements?
(4 answers)
Closed 1 year ago.
I have code that inputs data into db using OLEDB. Let's say it looks like this
var commandText = $"INSERT into {tableName}
({columnName1}, {columnName2})
VALUES ({value1, value2});"
var command = new OleDbCommand(commandText, connection);
command.ExecuteNonQuery();
Suggestion is to use OLEDB parameters, something like this
var commandText = $"INSERT into {tableName}
([{columnName1}], [{columnName2}])
VALUES (?, ?);"
var command = new OleDbCommand(commandText, connection);
command.Parameters.Add(new OleDbParameter(columnName1, value1));
command.Parameters.Add(new OleDbParameter(columnName2, value2));
command.ExecuteNonQuery();
What are the benefits of using parameters here?
Does it really improve security if the values are validated before?
With this code, no amount of parameters can help you.
The fact that you're concatenating the table name and the columns names makes your query vulnerable to SQL Injection attacks, which is the primary (but not only) reason to use parameters.
The way SQL injection works is by replacing parts of the string sent to the database with sql code. Parameters prevent that from happening because the database treats them as placeholders for data, which means it will not run any sql code that was passed through them.
However, since your table name and column names are also c# variables, they can be replaced with SQL code that the database will try to run.
Suppose the following code:
var tableName = "table (Col1) VALUES (null);DROP TABLE table;--";
// declare and populate columnName1, columnName2 with whatever values you want here, even just nulls
var commandText = $"INSERT into {tableName}
([{columnName1}], [{columnName2}])
VALUES (?, ?);"
// run SQL command here
This will insert a single record into table, and then drop the table from the database.
For more information, read my blog post entitled Back to basics: SQL Injection
Actually, yes. We all make mistakes and the extra safety added by the parameter object is like a second set of eyes.
Also, if you always use the parameter object, you run less of a risk in introducing errors down the line.

What's causing this syntax error? oledb INSERT statement

I posted about a different issue I had earlier, and with some help resolved that.
I am now receiving a syntax error centered around the following code:
importConnection.Open();
Console.WriteLine("Foxpro connection open");
OleDbCommand deleteOleDbCommand = new OleDbCommand(#"TRUNCATE TABLE CLIENT",
importConnection);
Console.WriteLine("writing to table");
Console.ReadKey();
using (OleDbCommand importCommand = new OleDbCommand(
string.Format("INSERT INTO CLIENT (Title,Fname,Sname)" + "VALUES ({0},{1},{2})",
exportReader.GetValue(0), exportReader.GetValue(1), exportReader.GetValue(2))))
importCommand.ExecuteReader();
With this being the output:
Output Text
using break points I have determined that the export reader calls are receiving data. I've narrowed it down to an issue with:
"VALUES ({0},{1},{2})",
exportReader.GetValue(0), exportReader.GetValue(1), exportReader.GetValue(2))))
I have confirmed that data can be inserted by doing the following:
using (OleDbCommand importCommand =
new OleDbCommand(string.Format(
"INSERT INTO CLIENT (Title,Fname,Sname)" + "VALUES (Mr,Joshua,Cameron-Mackintosh)",
importConnection)))
This causes no problems, so I know the issue does not lie with the underlying connection or command.
Others correctly comment about SQL-Injection, however VFP is less impacted to SQL-Injection, it can be just the same. Main reason, VFP doesn't really work with multiple queries the same way other sql engines allow by a ";" identifying break between statements. However, with mismatched quotes, it CAN and WILL break your sql-statements from actually running.
Having said that, VFP OleDb provider does allow parameterizing, but does so without "named" parameters. It does it with "?" as a place-holder for where the value would be inserted by the .net framework, and you don't have to convert the data type as long as it is in the same expected format (ex: string, numeric, date)
change your OleDbCommand to
"INSERT INTO CLIENT (Title, Fname, Sname ) values ( ?, ?, ? )"
Then, set your parameters via
importCommand.Parameters.Add( "parmForTitle", exportReader.GetValue(0));
importCommand.Parameters.Add( "parmForFName", exportReader.GetValue(1));
importCommand.Parameters.Add( "parmForSName", exportReader.GetValue(2));
Also, the parameters must be added in the exact same sequential order as they appear in the query. So, I prefixed them with "parmFor" to indicate it is the parameter placement for the corresponding field being inserted (or updated, or used in select, insert or delete too). The command objects work the same for all the commands. Even if you write a select statement and have values in a WHERE, JOIN or whatever other position.
THEN, ExecuteNonQuery()
It is saying "foxpro connection string" there. If it is done against
a VFP database, then "Truncate table client" wouldn't work in the
first place. That command does not exist in VFP. Instead you could
try using "Delete From Client" which marks the records for deletion.
Or you can use "zap" command with ExecSript that would correspond to
"truncate table" but then the connection needs to use the table
exclusively.
You should quote the string values. Better yet, for any SQL operation you should use parameters. When you use parameters, you should do that in a correct way for the connection you use. Here you are using an OLEDB connection, then you should use ? as a parameter place holder.
A revised version of your code would then be:
importConnection.Open();
Console.WriteLine("Foxpro connection open");
OleDbCommand deleteOleDbCommand = new OleDbCommand(#"Delete from CLIENT",
importConnection);
Console.WriteLine("writing to table");
Console.ReadKey();
using (OleDbCommand importCommand = new OleDbCommand(
#"INSERT INTO CLIENT (Title,Fname,Sname) VALUES (?,?,?)",
importConnection))
{
importCommand.Parameters.AddWithValue("title","");
importCommand.Parameters.AddWithValue("fname","");
importCommand.Parameters.AddWithValue("sname","");
// maybe in a loop here
importCommand.Parameters["title"].Value = exportReader.GetValue(0);
importCommand.Parameters["fname"].Value = exportReader.GetValue(1);
importCommand.Parameters["sname"].Value = exportReader.GetValue(2);
importCommand.ExecuteNonQuery();
// ...
}
PS: You could directly feed the values on the Parameters.AddWithValue instead of creating a separate .Parameters["..."].Value = ... but then you would only be able to do that for a single insertion (not something related to VFP, it is the case for OleDb or Sql or whatever).
You don't need ExecuteReader for an insert statement. Just use ExecuteNonQuery.
In your case, if your columns are character typed, you need to use single quotes with them for example;
VALUES ('{0}', '{1}', '{2}')
Also use white space (not have to but as a good practice) before VALUES part.
"INSERT INTO CLIENT (Title,Fname,Sname)" + " VALUES (Mr,Joshua,Cameron-Mackintosh)",
// ^^^ here
But more important;
You should always use parameterized queries. Prepared statements automatically handle for escape characters for example and this kind of string concatenations are open for SQL Injection attacks.
You must give space to your concatenated strings.
"INSERT INTO CLIENT (Title,Fname,Sname)" + "[space here]VALUES (Mr,Joshua,Cameron-Mackintosh)"
however, it should look like this:
"INSERT INTO CLIENT (Title,Fname,Sname) VALUES (?,?,?)"
Always make use of parametrized queries. Please refer to:
http://blogs.technet.com/b/neilcar/archive/2008/05/21/sql-injection-mitigation-using-parameterized-queries.aspx

Faster update for millions of records in Web Forms

So I need to update some over 1 million records from an asp.net web forms project in the code behind with a data given in a TextBox. I tried to do that with LINQ but that takes to long...
1st Question: What is the best solution?
I noticed that if I run the update in SQL (MSSQL) it only takes 20-30 seconds and that is an acceptable time.
2nd Question: Should I create procedures in SQL and import them in my project and call the procedures ? Will that give me a much better time? Basically, will using imported procedures bring the time down to close to the time needed for that query to run in SQL?
If running it via a normal query is faster, create a stored procedure that accepts a parameter:
using (SqlConnection con = new SqlConnection("connection string here")) {
using (SqlCommand cmd = new SqlCommand("sp_Stored_Proc_Name_Here", con)) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#VariableNameHere", SqlDbType.VarChar).Value = textBoxNameHere.Text;
con.Open();
cmd.ExecuteNonQuery();
}
}
Stored Procedures should be the best solution.
However, you can use ADO.NET to call these stored procedures without the need to import them in your Project.
Stored Procedures are SQL-Injection Safe
Use this to gain high-performance on your app:
1) Create a stored proc that accepts a TVP (Table-Valued-Parameter) when calling it.
That way, you call the proc only ONE TIME and flush ALL the data at once
You MUST use ADO.Net to call it (LINQ does not support TVPs)
Here are the resources:
Define the proc http://technet.microsoft.com/en-us/library/bb510489.aspx
Call from C# http://msdn.microsoft.com/en-us/library/bb675163(v=vs.110).aspx
2) Make sure your TVP uses a table-value-data-type that matches in column-data-types against the expected indexes of the target table (so SQL can effectively use indexes or anything required).
3) Use a correct transaction isolation level inside your proc. For example, "READ COMMITTED", which gives good response.
4) Add into your stored proc the "SET NOCOUNT ON", so the proc won't signal ADO.Net each time a T-SQL statement is executed.
5) On your SqlConnection object (in C#), set the PacketSize property to something between 3000 and 8000. Play with the values until you get the right response time. http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.packetsize.aspx

SqlCommand stored procedure without a named parameter

I've written a query parser that should create a SqlCommand and execute a stored procedure. The query for the stored procedure can come in many forms, including this one:
exec dbo.sp_StoredProcedureName 1599800
In this case, I create the SqlParameter this way:
var param = new SqlParameter() { Value = paramValue };
I get an error stating that '#Parameter1' is not a parameter for procedure sp_StoredProcedureName.
Is there a way I can do this without running it as a standard query? I'd like to keep it consistent and build the SqlCommand as a StoredProcedure type if possible.
I was thinking maybe I could reflect the parameter names of the stored proc first, but wondering if there's another approach.
Whilst there is a constructor for SQLParameter which doesn't set the name, you can't actually use a SQLParameter without setting the ParameterName property.
From MSDN (emphasis mine) :
The ParameterName is specified in the form #paramname. You must set
ParameterName before executing a SqlCommand that relies on
parameters.
Within your SP the parameter will have a name, look at the query's definition to find it.
If it's not known in advance what the name is going to be, try querying the sys.parameters table to find out what parameters a particular stored procedure takes.
It's better to call stored procedures specifying which parameter is which anyway (especially if there's more than one parameter):
exec dbo.sp_StoredProcedureName #myParam = 1599800
From C# you can add it by name once you know what your parameter is called:
cmd.Parameters.AddWithValue("#myParam", 1599800);
When you create a command of the type you specified in the question, this command gets converted to a dynamic SQL query which is somewhat similar to the following snippet:
EXECUTE sp_executesql 'exec dbo.sp_storedprocName #FirstParam=value, ...'
Here, #FirstName is name of the parameter you specify while adding it to the SqlCommand object. This type of query cannot be created without specifying names of parameters.
You can view the query that gets generated by ADO.NET using SQL Server profiler. It is a good practice to open SQL Server profiler and see how a query is interpreted, as it helps in avoiding common mistakes we do while writing queries in ADO.NET

Categories