Is it somehow possible to intercept the query that a given SqlCommand is going to execute on the database?
I'd like to track for debugging purposes all queries that my Data class invokes, and can't find a clever way to do this.
I tried to use some weird "replace" of the sql command string, or appending the parameters with a funky
sb.AppendLine("#" + p.ParameterName + " = " + p.ToDebugString());
(with "ToDebugString()" being an extension method that does a "ToString()" with or without single quotes depending if it's a string or not)
But that seems kinda unprofessional, and it fails horribly when it encounters an
SqlDbType.Structured
parameter.
More or less, I'd like to intercept the DB call inside the application in the same way the SqlServer Profiler does inside the DB itself.
Thank you in advance.
BIG EDIT:
I know that given a simple query like this:
SELECT * FROM MyTable WHERE ID=#ID
Rather than running it like this:
SELECT * FROM MyTable WHERE ID=1234
The database actually runs the procedure like this:
DECLARE #ID int
SET #ID = 1234
SELECT * FROM MyTable WHERE ID=#ID
Can I intercept at application level this last block?
It sounds like you're wanting to see the parameters substituted directly in the query string "as it's done on the server". This is not possible, because the server never substitutes the parameters into the string. That's the beauty of parameterized queries: data is data, code is code, and never that twain shall meet.
Given a simple query like this:
SELECT * FROM MyTable WHERE ID=#ID
Rather than running it like this:
SELECT * FROM MyTable WHERE ID=1234
You can think of it as if the database actually runs a procedure like this:
DECLARE #ID int
SET #ID = 1234
SELECT * FROM MyTable WHERE ID=#ID
Related
figured out answer and edited question so it works now.
Here is my example
CustomerID column in my database is set to uniqueidentifier.
C#
string cid = ( This is a variable; it could be ANY guid the user chooses. )
Query String
" SELECT * FROM [db]
WHERE (CAST(CustomerID AS VARCHAR(100)) LIKE '%"+cid+#"%');"
The CustomerID field does not evaluate for a rows with the same cid. So the cid is having trouble being evaluated with the uniqueidentifier (CustomerID).
Don't build your query by string concatenating user input into the query. It can lead to sql injection which is a serious issue.
Instead you should use parametrized queries, where you pass the user input to the query in the form of a parameter. The underlying framework/driver/etc will then make sure that the user input is prepared in a way that cannot accidentally become sql code.
Something like this:
Guid cidAsGuid = new Guid(cid);
MySqlCommand command = new MySqlCommand("SELECT * FROM [db] WHERE CustomerId = #customerID");
command.Parameters.AddWithValue("#customerID", cidAsGuid);
And then you can use the command to execute a reader or fill an adapter or some other thing you need to do.
This has the added benefit that an index on the CustomerId column can be used to speed up the query, whereas if you search by converting the CustomerId from a Guid to a string means the database will have to scan the whole table. For small tables you won't notice much difference but for large tables the difference will be significant.
I'm seeing a few possible issues;
There's no condition defined for the ISNULL e.g. ISNULL(Column,valueifnull).
Also the actual query must specify a column to use ISNULL in that structure.
There's a missing "closing bracket" on the ISNULL.
Something such as this should work:
SELECT ISNULL(
(
SELECT Column1 FROM MyTable
WHERE (CAST(CustomerID AS VARCHAR(100)LIKE '%"+cid+#"%')
)
,'')
You don't need like if CustomerID is uniqueidentifier too.
WHERE (CAST(CustomerID AS VARCHAR(36)) = #cid)
I used parameters in the whereclause, but what about the variables for this {0}. Do I need to create a parameter for it to prevent sql injection?
("...inner join db1.dbo.table1.id on db2.dbo.table2.id = {0}.dbo.table3.id where name=#name",abc)
var abc = ddl2.SelectedItem.Text;
cmd.Parameters.AddWithValue("#name", ddl1.selectedvalue);
To the best of my knowledge, you can't actually 'paramaterize' database names/table names.
String.Format does not solve SQL injection in this case since it is possible for the user to change ddl2.SelectedItem.Text to whatever they want.
If you need a dynamic value for the database name, I suggest you either keep that value as a const or store it somewhere that you have complete control over/ is never sent or interpreted client side.
I would suggest you to use any ORM (object relational mapping) i.e Entity framework or n-hibernate etc. and use linq to write queries, that will prevent you application from SQL Injection.
Unfortunately, as Abbath already mentioned, this type of construct is not parameterizable. As Abbath mentioned, the best solution is to keep such arguments under your absolute control, but there are times, where such constructs are needed, and it may not be possible to have complete control over them.
For such scenarios, the best recommendation in this case is to escape the arguments. In this case, the DB name represented by {0} on your sample code.
There are two potential mechanisms to achieve this:
a) Create a mechanism that allows you to parameterize the query
Advantage: You can reuse the same solution from any driver (.Net, ODBC, etc.)
Disadvantage: A bit more work. You will not use select directly anymore on this case.
For example (I am including a simple example that has an inner join, just like your code):
CREATE PROC sp_MyQuery( #target_db_name sysname, #name nvarchar(100))
AS
BEGIN
DECLARE #cmd nvarchar(max)
DECLARE #parameters nvarchar(max)
SELECT #cmd = N'SELECT * FROM msdb.sys.objects inner join '
+ quotename(#target_db_name) + N'.sys.sql_modules
on msdb.sys.objects.object_id = '
+ quotename(#target_db_name) + N'.sys.sql_modules.object_id WHERE name = #name'
print #cmd -- See the command before it is executed.
set #parameters = N'#name nvarchar(100)'
EXEC sp_executesql #cmd, #parameters, #name = #name
END
go
-- Example of usage
DECLARE #target_db_name sysname = 'msdb'
DECLARE #name nvarchar(100) = 'sp_help_operator'
EXEC sp_MyQuery #target_db_name, #name
go
At this point, you can use SqlParameter objects as you would normally do. For example:
sqlcmd.CommandText = #"[dbo].[sp_MyQuery]";
sqlcmd.CommandType = System.Data.CommandType.StoredProcedure;
sqlcmd.Parameters.AddWithValue("#target_db_name", ddl0.selectedvalue);
sqlcmd.Parameters.AddWithValue("#name", ddl1.selectedvalue);
SqlDataReader reader = sqlcmd.ExecuteReader();
b) Escape the DB name within your CLR code
Advantage: Easier to implement
Disadvantage: App-specific solution, you need to be careful with potential Unicode–DB collation translation issues.
For example (same query as above):
sqlcmd.CommandText = String.Format(#"
SELECT * FROM msdb.sys.objects inner join [{0}].sys.sql_modules on msdb.sys.objects.object_id = [{0}].sys.sql_modules.object_id WHERE name = #name;",
ddl0.selectedvalue.Replace("]", "]]"));
sqlcmd.CommandType = System.Data.CommandType.Text;
sqlcmd.Parameters.AddWithValue("#name", ddl1.selectedvalue);
SqlDataReader reader2 = sqlcmd.ExecuteReader();
I typically recommend using solution (a) whenever it is possible, but both solutions should help you protect against SQL injection.
BTW. The following link may also be quite useful: https://blogs.msdn.microsoft.com/raulga/2007/01/04/dynamic-sql-sql-injection/
I hope information helps.
you dont need create parameter for abbc if you use string.format like this
var abc = ddl2.SelectedItem.Text;
string.format("...inner join db1.dbo.table1.id on db2.dbo.table2.id = {0}.dbo.table3.id where name=#name",abc)
cmd.Parameters.AddWithValue("#name", ddl1.selectedvalue);
I've encountered quite a wierd problem:
I form SQL server command dynamically and one of its part is a group of LIKE tests, that are used as textual search on several columns and tables. It looks like:
SET #text = '%' + REPLACE(REPLACE(REPLACE(#text, '!', '!!'), '%', '!%'), '_', '!_') + '%'
INSERT INTO
#textFiltered
FROM
#documents d
LEFT JOIN docData t1 WITH (NOLOCK)
ON t1.ID = d.ID AND
(t1.Topic like #text escape '!'
OR t1.Subject like #text escape '!')
LEFT JOIN docData2 t2 WITH (NOLOCK)
ON t2.ID = d.ID AND
t2.Name like #text escape '!'
WHERE
t1.ID IS NOT NULL
OR t2.ID IS NOT NULL
(Surely, that's not the best way to do textual search, still that's not the point)
Now, when I create SQLCommand in C#, like this:
using (var cmd = new SqlCommand())
{
cmd.CommandText = cmdText;
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
cmd.Parameters.Add("text", NVarChar, 4000).Value = searchText;
var reader = cmd.ExecuteReader();
....
}
performce of executing is very poor (say, 8 seconds), while executing same query in SQL Management Studio much faster (say, 500 ms).
However, if instead of passing text as parameter, I embed it into text of SQL with:
DECLARE #text nvarchar(max)
SET #text = '<embedded-text>'
then SqlCommand also runs fast.
What's even more strange this behavior seems to be correlated with set of columns that are used in LIKE clauses (didn't figure how). Types of those columns can be nvarchar(size), nvarchar(max), ntext.
I suspect that problem is with parameter - maybe it's type is incorrect or smth else.
P.S. Tried to create parameter with size = length of searched text + 1 - didn't help.
My best guess will be that it is related to the query plan that SQL Server is choosing for you.
there is big difference between using constant varchar which the SQL server can evaluate against existing statistics, and using arbitrary variable which the server know nothing about.
you can try OPTION (RECOMPILE) hint. although this hint will cause the Stored procedure to be compiled on every call, it'll also allow the SQL Server to search for the best plan for the given values, and maybe in your case it will be good trade off.
you could also add the query plan for both option of the stored procedure, and maybe someone will be able to see the difference, and pin point the exact problem.
how to use table hints in Linq to sql when call submit changes method
dataContext.table2.something = dataContext.table1.something;
dataContext.SubmitChanges();
i want to use it like this sql code:
declare #var int;
begin transaction
select #var = something from table1 with (HoldLock);
update table2 set something = #var;
update table1 set something = #var + 1;
commit transaction;
This is not possible.
Actually, it is possible by doing serious nasty hacking using reflection. You can compile a query, and then fiddle with the generated SQL string in some internal object. This is the least desirable way to do this.
I recommend you stay with raw SQL for this one.
I've always heard that cant be done. Linq's goal (or at least one of them) is take or mind out of SQL so you dont have to worry with things like this. I suggest that you add your code with the table hint to a SQL procedure and use Linq to call it.
In both queries 1 and 2, the text from the textbox is inserted into the database. What's the significance of the parameterized query here?
Passing txtTagNumber as a query parameter
SqlCommand cmd = new SqlCommand("INSERT INTO dbo.Cars " +"VALUES(#TagNbr);" , conn);
cmd.Parameters.Add("#TagNbr", SqlDbType.Int);
cmd.Parameters["#TagNbr"].Value = txtTagNumber.Text;
Converting txtTagNumber to an integer before constructing the query
int tagnumber = txtTagNumber.Text.ToInt16(); /* EDITED */
INSERT into Cars values(tagnumber.Text); /* then is it the same? */
Also, here I would use Regular Expression validation to stop insertion of illegal characters.
Parameterized queries do proper substitution of arguments prior to running the SQL query. It completely removes the possibility of "dirty" input changing the meaning of your query. That is, if the input contains SQL, it can't become part of what is executed because the SQL is never injected into the resulting statement.
Imagine a dynamic SQL query
sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND
Pass=' + password
so a simple sql injection would be just to put the Username in as ' OR
1=1-- This would effectively make the sql query:
sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS='
+ password
This says select all customers where they're username is blank ('') or
1=1, which is a boolean, equating to true. Then it uses -- to comment
out the rest of the query. So this will just print out all the
customer table, or do whatever you want with it, if logging in, it
will log in with the first user's privileges, which can often be the
administrator.
Now parameterized queries do it differently, with code like:
sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'
parameters.add("User", username) parameters.add("Pass", password)
where username and password are variables pointing to the associated
inputted username and password
Now at this point, you may be thinking, this doesn't change anything
at all. Surely you could still just put into the username field
something like Nobody OR 1=1'--, effectively making the query:
sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND
Pass=?'
And this would seem like a valid argument. But, you would be wrong.
The way parameterized queries work, is that the sqlQuery is sent as a
query, and the database knows exactly what this query will do, and
only then will it insert the username and passwords merely as values.
This means they cannot effect the query, because the database already
knows what the query will do. So in this case it would look for a
username of "Nobody OR 1=1'--" and a blank password, which should come
up false.
Source: lavamunky.com; Nov 2011
SQL injection happens when a possible parameter has SQL within it and the strings are not handled as it should be
eg:
var sqlquerywithoutcommand = "select * from mytable where rowname = '" + condition+''";
and the condition is a string coming from the user in the request. If condition is malicious
say eg:
var sqlquerywithoutcommand = "select * from mytable where rowname = '" + "a' ;drop table mytable where '1=1"+"'";
you could end up running malicious scripts.
However, using parameters the input will be cleaned of any characters which might escape string characters, which means you can be ensured no matter what comes in it will not be able to run inject scripts.
Using the command object with parameters the SQL actually executed would look like this:
select * from mytable where rowname = 'a'';drop table mytable where 1=1'''
in essence it will be looking for a row with rowname = a';drop table mytable where 1=1'
and not running the remaining script.
Parameterized queries handles everything - why go to the trouble?
With parametrized queries, in addition to general injection, you get all the data types handled, numbers (int and float), strings (with embedded quotes), dates and times (no formatting problems or localization issues when .ToString() is not called with the invariant culture and your client moves to a machine with and unexpected date format).
Parameterized queries allow the client to pass the data separately form the query text.
Where on most free from text you would do validation + escaping.
Of course Parameterization don't help against other kind of injection, but as the parameter are passed separately, they are not use as execution text query.
A good analogy would be the "recent" execution bit used with most of the modern processor and Operating system to protect from buffer overflow. It still allows the buffer overflow but prevent the execution of the injected data.
It is quite understandable why one would feel so.
sqlQuery = "select * from users where username='+username+';"
vs
sqlQuery = "select * from users where username=#username;"
Both the above queries seem to do the same thing.But they actually don't.
The former uses input to for a query, the latter decides on the query but only substitutes the inputs as it is during the execution of the query.
To be more clear, the parameters' values are located some where on the stack where the variables' memory is stored and is used for search when needed.
So if we were to give ' OR '1'='1 as the input in username, the former would dynamically construct a new queries or queries as part of the sql query string sqlQuery which is then executed.
While on the same input, latter would search for ' OR '1'=' in the username field of the users table with the statically specified query in the query string sqlQuery
Just to consolidate it, this is how you use parameters to make query:
SqlCommand command = new SqlCommand(sqlQuery,yourSqlConnection);
SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "#username";
parameter.Value = "xyz";
command.Parameters.Add(parameter);