Proper Way to Format SQL Query Within C# Application - c#

I have a C# console application that makes a bunch of queries to a database server.
I frequently need to modify the SQL and would like to simply copy/paste the SQL from my SQL Editor into my C# source without having to reformat the SQL every time.
Currently, the SQL is all on one line... like below:
OleDbDataAdapter da_ssm_servers = new OleDbDataAdapter(#"SELECT * FROM mytable ORDER BY Server;", connSSM);
The SQL is much longer than above with a lot of table JOINS, etc.
I would like to keep the formatting, but don't really want to have to go back and add quotes around each line, etc.
If anyone has any recommendations and examples, it would be appreciated.

I do it like this:
string sql = #"
SELECT *
FROM mytable
ORDER BY Server";
OleDbDataAdapter da_ssm_servers = new OleDbDataAdapter(sql, connSSM);

My recommendation would be to stay away from ad hoc queries like you are using and utilize stored procedures. That would decouple your design, as well as limit your calls to a stored procedure name and possibly parameters.
But if you must use ad hoc queries then prefix with # and you will be able to span multiple lines without having to surround each line in quotes.

As long as you use the # syntax then you can have the SQL span multiple lines just fine.
For example
string sql = #"select
colA,
colB,
colC
from
tableX
inner join tableY
on tableX.colA = tableY.colA
where
colB > 20;"

It seems that your code should work fine. When you start your string with # then you're using a verbatim string. Line breaks and formatting are preserved and you don't need to enclose each line in quotes. You just need an ending quote.

Related

SQLInjection with Npgsql/Postgres

I want to learn how SQLInjection is working with the PostgresqlDb. I am using the Npgsql in C#.
So this is my Query, which is called, when I rename a folder:
cmd.CommandText = "UPDATE allfolder.folder SET folder_name = '" + foldernamenew + "'";
cmd.ExecuteNonQuery();
I now tried to pass the following value into the textfield:
abcdef; INSERT INTO allfolder.folder (id, folder_name) VALUES (56,"aaaaaaa");
Then AJAX is fired.
The output I assumed is, that all folders in the table has the folder_name "abcdef" and that I have a new folder with the id "56" called "aaaaaa". This is not the case, because the semicolon is not recognized as delimiter and so the name of each folder_name are "abcdef; INSERT INTO ....". I do not want to change it on DB side that multiqueries are allowed.
So my questions are:
How do I need to prepare the SQL statement, that I achieve a Injection?
Is an injection even possible, when you are not allowed to execute a second qry because the semicolon isn't recognized?
I am only talking about Npgsql and postgres.
As Laurenz mentioned above, this should work:
abcdef'; INSERT INTO allfolder.folder (id, folder_name) VALUES (56,'aaaaaaa'); -- test
Make sure, that you don't mix single and double quotes. A common mistake in SQL. You need the single quote before the semicolon, otherwise it is in the textstring, because you have no delimiter.
“Multiqueries” (several queries in one line, separated by semicolon) are always allowed in PostgreSQL.
Your problem are the multiple errors in your SQL:
Missing single quote after abcdef.
Double quotes instead of single quotes around aaaaaaa.
No single quote or line comment at the end of the statement.
Speaking pointedly: you have to learn SQL before you can learn SQL injection.

table name sql injection

I am working with C#. I need to write a select inline query.
The table name should be taken from config. I cannot write a stored procedure.
SqlCommand myCommand= new SqlCommand();
myCommand.CommandText = "Select * from " + tableName;
myCommand.CommandType = CommandType.Text;
myCommand.Connection = connString;
How to avoid sql injection ?
Just create a query with a real param and check for the existence of the tablename - somthing like:
SELECT COUNT(*) FROM SYS.TABLES WHERE NAME = #pYOURTABLENAME
IF that returns 1 then you know that the table exists and thus can use it in the SELECT you showed in the question...
However I strongly recommend to try anything to get rid of the need for any code prone to SQL injection!
I would ensure table name contains only these characters:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz[]. -_0123456789
E.g.,
Regex regex = new Regex(#"^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\[\]. -_0123456789]{1,128}$");
if (!regex.IsMatch(tableName)) throw new ApplicationException("Invalid table name");
To do a more comprehensive job including non-English languages see this reference on what a valid table names:
http://msdn.microsoft.com/en-us/library/ms175874.aspx
You need to verify that tableName is appropriate. After some sanity checking (making sure it has no spaces, or other disallowed characters for table names, etc), I would then first query the database for the names of all tables, and programmatically verify that it is one of those table names. Then proceed to run the query you show.
I'd look at moving the SQL to a stored proc and review this article by Erland Sommarskog as it has some great ideas for using dynamic SQL within stored procs. I'd certainly look into it. It discusses a lot of the issues around SQL injection and possible alternatives to dynamic SQL.
He also has another great article on ways to use arrays in stored procs. I know you didn't ask for that, but I often refer to these two articles as I think they are quite insightful and provide you with some useful ideas with regards to writing your procedures.
In addition to some of the suggestions linked above, I still have some basic parameter sanitisation mechanisms that I use if I am ever using dynamic SQL. An example of this is as follows;
IF LEN(#TableName) < 5 OR LEN(#TableDisplayName) < 5
BEGIN
RAISERROR('Please ensure table name and display name are at least 5 characters long', 16, 1)
END
IF NOT (#TableName not like '%[^A-Z]%')
BEGIN
RAISERROR('The TableName can only contain letters', 16, 1)
END
IF NOT (#TableDisplayName not like '%[^0-9A-Z ]%')
BEGIN
RAISERROR('The TableDisplayName can only contain letters, numbers or spaces', 16, 1)
END
This combined with using parameters within your dynamic sql and then executing using sp_executesql certainly help to minimise the possibility of a SQL injection attack.

Searching SQL Server with LIKE Operator

I have a problem when I try to read rows from SQL Server 2005 from code in C#
The idea:
In my database (SQL Server 2005 Express) there is a table with a column (of datatype ntext) containing HTML code.
In my C# application user can enter a sentence (HTML code) and search the rows with contains this sentence.
The query generated from my app is:
USE test
SELECT
al.aal_Id As ID,
al.aal_Description As Opis,
au.au_Title As Tytul_szablonu,
au.au_Note As Nazwa_szablonu
FROM dbo.au_Allegro al
LEFT OUTER JOIN dbo.au__Auction au ON (al.aal_AuctionId = au.au_Id)
WHERE
au.au_Type = 11
AND al.aal_Description COLLATE SQL_Latin1_General_CP1_CS_AS LIKE '%%' ESCAPE '\'
In my App I'm converting special characters (e.g. ',) and adding escape character.
User tries to search for very long sentence (about 7000+ chars), when he tries to do this the sqlserver.exe process consumes all of his RAM memory and search time is about 30+ minutes (he has about 1000+ rows in this table).
The query returns 0 rows.
When he tries to run (this same) query in SQL Server Management Studio the database shows results in few seconds (with rows).
In my app I use SqlDataAdapter:
System.Data.DataTable dt = new System.Data.DataTable();
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand();
cmd.CommandTimeout = 0;
cmd.Connection = conn;
System.Data.SqlClient.SqlDataAdapter da = new System.Data.SqlClient.SqlDataAdapter(kwerenda, conn);
try
{
da.Fill(dt);
}
I tried SqlDataReader:
dr = cmd.ExecuteReader();
while (dr.Read())
{
string id = dr["ID"].ToString();
string opis = dr["Opis"].ToString();
string tytul = dr["Tytul_szablonu"].ToString();
string nazwa = dr["Nazwa_szablonu"].ToString();
dt.Rows.Add(id, opis, tytul, nazwa);
}
When I tried to simulate this in my test database I don't have any problems with search (this same) sentences.
Have you got any tips for me ?
I can't do any changes in user datatable, i can't go to him and check what happens.
Is the SQL command executing a stored procedure? If so you might be getting different query plans, which may explain the timing difference between the apps. Your ADO.Net call might be affected by something known as parameter sniffing, which can cause radically different query execution times.
There are a couple of things you can do to avoid this problem and yield consistent results.
Convert parameters to local variables inside of the stored procedure.
Disable the feature on the SQL server altogether.
Also your syntax looks suspect as John pointed out. It would be better to use a NVARCHAR(MAX) datatype for that column if possible NTEXT should be avoided as its been deprecated.
A better alternative to doing like searches on a non-indexed column like this is to utilize the SQL's Full Text Search which is optimized for these types of queries.
http://msdn.microsoft.com/en-us/library/ms142571.aspx
http://www.developer.com/article.php/3446891
A couple of things you might want to do.
First, don't use nText. SQL 2005 has a datatype called nvarchar(max). It's MUCH better for storing large amounts of text. Further, ntext was deprecated so save yourself some trouble and convert it now. See this link on how to successfully do this.
Second, the query you posted is unusual. You have a left outer join, but you have a where clause on the outer joined table. Because of the where clause it's being converted (hopefully) into an inner join. You should just write it that way OR move the au.au_type = 11 to be part of the join construct. I doubt you want the latter.
Third, when the client runs the query the first time through your app it is generating a query plan based on those parameters. Running the exact same query shortly thereafter in Management Studio is going to reuse that plan and cached data. Therefore the second pass will be fast so no surprise there.
Fourth, I don't think you posted the actual query that was run. I suspect there is some data in the parameter you are comparing which either isn't escaping properly OR is using one of the reserved characters such as '[', ']', ^, etc.

How to escape simple SQL queries in C# for SqlServer

I use an API that expects a SQL string. I take a user input, escape it and pass it along to the API. The user input is quite simple. It asks for column values. Like so:
string name = userInput.Value;
Then I construct a SQL query:
string sql = string.Format("SELECT * FROM SOME_TABLE WHERE Name = '{0}'",
name.replace("'", "''"));
Is this safe enough? If it isn't, is there a simple library function that make column values safe:
string sql = string.Format("SELECT * FROM SOME_TABLE WHERE Name = '{0}'",
SqlSafeColumnValue(name));
The API uses SQLServer as the database.
Since using SqlParameter is not an option, just replace ' with '' (that's two single quotes, not one double quote) in the string literals. That's it.
To would-be downvoters: re-read the first line of the question. "Use parameters" was my gut reaction also.
EDIT: yes, I know about SQL injection attacks. If you think this quoting is vulnerable to those, please provide a working counterexample. I think it's not.
I was using dynamic sql (I can hear the firing squad loading their rifles) for search functionality, but it would break whenever a user searched for somebody with a surname like "O'Reilly".
I managed to figure out a work-around (read "hack"):
Created a scalar-valued function in sql that replaced a single quote with two single quotes, effectively escaping the offending single quote, so
"...Surname LIKE '%O'Reilly%' AND..."
becomes
"...Surname LIKE '%O''Reilly%' AND..."
This function gets invoked from within sql whenever I suspect fields could contain a single quote character ie: firstname, lastname.
CREATE FUNCTION [dbo].[fnEscapeSingleQuote]
(#StringToCheck NVARCHAR(MAX))
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE #Result NVARCHAR(MAX)
SELECT #Result = REPLACE(#StringToCheck, CHAR(39), CHAR(39) + CHAR(39))
RETURN #Result
END
Not very elegant or efficient, but it works when you're in a pinch.
One may wish to replace ' with '' instead of parameterizing when needing to address the ' problem in a large amount of ad hoc sql in a short time with minimal risk of breakage and minimal testing.
SqlCommand and Entity Framework use exec sp_executesql....
So there really is an alternative to raw strings with your own escaping pattern presumably. With SqlCommand you are technically using parameterised queries but you're bypassing the ADO.Net abstraction of the underlying SQL code.
So while your code doesn't prevent SQL Injection, the ultimate answer is sp_executesql not SqlCommand.
Having said that, I'm sure there are special handling requirements for generating an SQL Injection-proof string which utilizes sp_executesql.
see: How to return values from a dynamic SQL Stored Procedure to the Entity Framework?
Simple:
const string sql = "SELECT * FROM SOME_TABLE WHERE Name = #name";
and add the #name parameter with value:
cmd.CommandText = sql;
cmd.Parameters.AddWithValue("#name", name);
If you need to escape a string for a MSSQL query try this:
System.Security.SecurityElement.Escape(Value)

SQL injection on INSERT

I have created a small survey web page on our company Intranet. This web page is not accessible from the outside.
The form is simply a couple of radio buttons and a comments box.
I would like to maintain good coding practices and would like to guard against SQL Injections.
Can SQL injections happen on a insert statement with comments from the textbox?
If so, how can I guard against it using .NET 2.0?
Injection can happen on any SQL statement not run properly.
For example, let's pretend your comment table has two fields, an integer ID and the comment string. So you'd INSERT as follows:
INSERT INTO COMMENTS VALUES(122,'I like this website');
Consider someone entering the following comment:
'); DELETE FROM users; --
If you just put the comment string into the SQL without any processesing this could turn your single INSERT in to the following two statements followed by a comment:
INSERT INTO COMMENTS VALUES(123,''); DELETE FROM users; -- ');
This would delete everything from your users table. And there are people willing to spend all day finding the right tablename to empty using trial and error and various tricks. Here's a description of how you could perform an SQL Injection attack.
You need to use parameterized SQL statements to prevent this.
And this isn't just for security reasons. For example, if you're creating your SQL statements naively the following comment:
I'm just loving this website
would cause an SQL syntax error because of the apostrophe being interpreted by SQL as a closing quote.
Use parameterized queries so that the text is automatically quoted for you.
SqlCommand command = connection.CreateCommand();
command.CommandText = "insert into dbo.Table (val1,val2,txt) values (#val1,#val2,#txt)";
command.AddParameterWithValue( "val1", value1 );
command.AddParameterWithValue( "val2", value2 );
command.AddParameterWithValue( "txt", text );
...
SQL injection can happen any time you pass a query back to the database. Here's a simple demonstration:
SQL Injection Explained
The key, within .NET, is to do as Dave Webb has given. It will prevent the injection attempt by encompassing the entire string as one parameter to be submitted, handling all characters that might be interpreted by SQL Server to change the query or append additional commands.
And it should be pointed out that SQL injection can occur on any application, not just web applications. And that an internal attack is usually the most costly to an organization. One cannot safely assume that an attack won't originate from within.
In addition to using prepared statements and parameters rather than concatenating strings into your SQL you should also do the following:
Validate and format user input on the server side. Client side validation and limits can easily be bypasses with tools like WebScarab, or by spoofing your form.
Configure appropriate permissions for the database user account. Web application should use a separate account or role in your database with permissions restricted to only the tables, views and procedures required to run your application. Make sure that user does not have select rights on the system tables
Hide detailed error messages from users, and use less common names for your objects. It amazes me how often you can determine the server type (oracle, mysql, sqlserver) and find basic schema information in an error message and then get information from tables called 'user(s)', 'employee(s)'. If you haven't set your permissions as in (2) and I can determine your server type you are open to statements like this for SQL Server
SELECT table_name FROM information_schema.table
EXECUTE sp_help foundTableName
Yes, they can happen. The easiest way to guard against this is to use prepared statements rather than building the SQL manually.
So, rather than this:
String sql =
String.Format("INSERT INTO mytable (text_column) VALUES ( '{0}' )",
myTextBox.Text); // Unsafe!
You would do something like this:
String sql = "INSERT INTO mytable (text_column) VALUES ( ? )"; // Much safer
Then add the text of the text box as a parameter to your DbCommand which will cause it to be automatically escaped and replace the "?" in the SQL.
Prevent SQL Injection by using prepared statement. The use of placehoder(?) totally eliminates sql Injection Vulnerability.
example
String sql=Select * from user_table where username='+request.getparameter("username")+';
statement.executeQuery(sql);
the above statement is vulnerable to sql injection.
To make it safe against sql injection.
Use following the snippet
String sql=Select * from user_table where username=?;
statement.setString(1,username);
Yes, it can. Let's say the client sends this:
OR 1 = 1
That can be very painfull for your
SELECT * FROM admin WHERE name = #name AND password = #password
You can prevent this with
using Parameter class from ADO.NET
using regexp
reading, reading: http://www.codeproject.com/KB/database/SqlInjectionAttacks.aspx
The easiest way to guard against that form of SQL injection, is to use parameters and stored procedures rather then building sql statements to run. (In C# or internally to SQL Server).
However I'm not entirely sure you should be spending time on this, unless of course it's your corporate policy, as the chances of it ever occuring internally are minimal at best, and if it did occur, I would hope you would know immediately who it is.

Categories

Resources