Inserting values directly into db-syntax in C# - c#

INSERTing values without parameters is fully understandable why it shouldn't be allowed, where you e.g. want to prevent sql-injection. However I do not understand why it's still a big no doing the following as well:
cmd.CommandText = "SELECT * FROM [Students]
WHERE StudentID = " + studentID + ";";
int getID = (int)cmd.ExecuteScalar();
What's the harm in it when just SELECTing? I don't really understand the point with parameters below. I'm not questioning it, I just want to know the reason why parameters is necessary and what consequences I could get from the code above instead using the option below.
var pStudentID = new SqlParameter("#studentID", SqlDbType.Int);
pStudentID.Value = studentID;
cmd.Parameters.Add(pStudentID);

There are two reasons it's better to use parameters.
Sql Injection - Your first example would be susceptible to a sql injection attack. What this means is if the studentID was being input from a web form, some one could use a '-- to comment out the select string and issue other commands against the database.
Prepare - If you use parameters you can prepare the sql statement, which is sort of a precompile of the syntax. This can be slightly more performant in high volume situations.
Edit: I came across this video on reddit the other day, which is a great example of how sql injection works.sql injection

Assume this input:
var studentID = "''; drop table users;--"
cmd.CommandText = "SELECT * FROM [Students]
WHERE StudentID = " + studentID + ";";
This would if calling this select delete the table users completely.
Parameters would help by approving only legitimate input to be added to the query.

Related

Dynamic build query & SQL Injection & DLLs files

I have read a lot of things about SQL injection and many arguments why you should avoid to build your query dynamically using plain code/concatenations within your cs.file
However, I have question and I need some advice from someone with more experience than me.
I created some DLL files in order to re-use my code in different projects and for that reason I was thinking generic.
I created these DLL files that contained all the logic/code of building SQL queries in a dynamic way + concatenation of statement. Thereafter, I add these DLL files as reference to my project.
This will be vulnerable to SQL Injection ? also is that insufficient procedure (time consuming/insufficient maintenance)?
Any advice would be appreciated.
If you are not processing your input that you are passing to your query (built at run time) you are vulnerable to SQL injection. Adding to dll or not doesn't makes any difference.
To overcome this you need to use parameterised queries. They have multiple advantages a part from security.
One reason that i can think of right now is that you have a text box. And your query is
"select * from table1 where name = '" + textbox1.Text;
Not lets assume that in textbox1 user enters Ehsan's. Your query will go bang and won't even execute.
Example of parameterised query
"select * from table1 where name = #Name"
yourCommand.Parameters.AddWithValue("#Name", textbox1.Text);
It's difficult to know for sure but if you do something like
string sql = "SELECT Field FROM Table WHERE Field = " + Somevar;
Then you are open to SQL injection if somevar comes from input of some kind (usually user input).
There is just no reason to do it as you can just do
string sql = "SELECT Field FROM Table WHERE Field = #myvar"
You should make sure you are using parameterized queries or stored procedures, etc.
And avoid using dynamic SQL queries such as
"SELECT * FROM Users WHERE UserID = " + UserID
as these are vulnerable to SQL Injection
Instead use a parameterized query or a stored procedure, e.g.
"SELECT * FROM Users WHERE UserID = #UserID"
Most ORMs (eg .NET's Entity Framework) will provide some protection against SQL Injection (but only if used properly)
this link explains in more detail http://www.troyhunt.com/2013/07/everything-you-wanted-to-know-about-sql.html

How to protect against SQL Injection with OdbcCommand when the query is not parameterized?

I am writing a generic sqldump utility that takes a DSN and a table name and dumps the contents to a file. It's an internal app so SQL Injection is not a serious threat, but I don't want to have to worry about it. The thing is, the variable part of the query is actually the tablename, so the query is going to look like:
select * from [tablename];
...which I don't imagine will work well with the OdbcCommand's parameterized query support. I am also trying to support all types of DSN's as generically as I can, regardless of the driver on the other side of the DSN.
Is there some universal way to sanitize my tablename input to protect against all SQL Injection using the OdbcCommand object?
I'd check the user input against the list of tables you know are there, using code roughly like what's posted here to retrieve the table list (code from the link included for posterity):
class Program
{
static void Main(string[] args)
{
string connectionString = GetConnectionString();
using (SqlConnection connection = new SqlConnection(connectionString))
{
// Connect to the database then retrieve the schema information.
connection.Open();
DataTable table = connection.GetSchema("Tables");
// Display the contents of the table.
DisplayData(table);
Console.WriteLine("Press any key to continue.");
Console.ReadKey();
}
}
That said, I agree with #KeithS above. This is probably a Bad Idea.
The only special character in a [] quoted identifier for SQL Server is ], and it can be escaped by passing ]]. So for that, "select * from [" + tableName.Replace("]", "]]") + "];" should be safe. Other systems, however, may use other escape mechanisms, so this is not a full solution if you want to connect to a different type of database.
Alternatively, consider each character, and see if it is a valid character for table names you wish to support. If you say table names only contain letters, digits, and/or whitespace, then SQL injection is not possible, because you'll never be able to unquote the [quoted table name].
You could first query the information_schema to find out if the table exists:
select *
from information_schema.tables
where table_schema = #your_database_name and table_name = #table_name
This query can be parameterized and is NOT prone to SQL injections.
Following that, you can issue your select * from #table_name query.
If the table name is enclosed in [ ] then just do not allow table names to contain "]". ] could be used by malicious people to terminated the sql command and to introduce dangerous code.
If you are constructing the sql like this
string sql = "SELECT * FROM [" + tablename + "]";
and the tablename is defined like this
string tablename = "tablename]; DELETE FROM [tablename";
The resulting sql becomes
SELECT * FROM [tablename]; DELETE FROM [tablename];
However, this is only possible if the table name contains a ].
Note:
If you are replacing string values like this, then replacing a single quote by two single quotes makes it safe too.
string sql = "SELECT * FROM tbl WHERE Name = '" + input.Replace("'","''") + "'";

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.

is it safe using dynamic SQL with parameters? If not, what security issues might it be exposed to?

For example, this is the code that I am using:
String commandString = "UPDATE Members SET UserName = #newName , AdminLevel = #userLevel WHERE UserID = #userid";
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["sqlconnectionstring"].ConnectionString))
{
SqlCommand cmd = new SqlCommand(commandString, conn);
cmd.Parameters.Add("#newName", newName);
cmd.Parameters.Add("#userLevel", userLevel);
cmd.Parameters.Add("#userid", userid);
conn.Open();
cmd.ExecuteReader();
Reader.Close();
}
That code looks fine. Parameterisation is the way to go, as opposed to concatenating user-supplied values in an adhoc SQL statement which can open you up to sql injection attacks. This can also help with execution plan reuse.
The only thing I'd add, is I prefer to explicitly define the datatype and sizes of the parameters. For example, if you don't then, as an example, all string values will get passed in to the database as NVARCHAR instead of VARCHAR. Hence I like to be explicit.
It's safe against SQL injection because it's parameterized. Other security concerns, such as ensuring that #userid is not spoofed, are separate security concerns that should be dealt with in other layers of your application.
That's still a static query string. It's not really "dynamic" sql until you also build parts of the string on the fly — something like this:
var sql = "SELECT columns FROM Table WHERE 1=1";
if (!string.IsNullOrEmpty(txtName.Text)) sql += " AND Name LIKE '%' + #Name + '%'";
if (!string.IsNullOrEmpty(txtDesc.Text)) sql += " AND CONTAINS(DESCRIPTION, #description)";
But even so, this is still "safe" in the sql injection sense as long as you continue to use parameters for every part of the query that originates with user input.

C# SqlCommand - cannot use parameters for column names, how to resolve?

Is there any way how to do that? This does not work:
SqlCommand command = new SqlCommand("SELECT #slot FROM Users WHERE name=#name; ");
prikaz.Parameters.AddWithValue("name", name);
prikaz.Parameters.AddWithValue("slot", slot);
The only thing I can think of is to use SP and declare and set the variable for the column. Seems to me a bit ackward.
As has been mentioned, you cannot parameterise the fundamental query, so you will have to build the query itself at runtime. You should white-list the input of this, to prevent injection attacks, but fundamentally:
// TODO: verify that "slot" is an approved/expected value
SqlCommand command = new SqlCommand("SELECT [" + slot +
"] FROM Users WHERE name=#name; ")
prikaz.Parameters.AddWithValue("name", name);
This way #name is still parameterised etc.
You cannot do this in regular SQL - if you must have configurable column names (or table name, for that matter), you must use dynamic SQL - there is no other way to achieve this.
string sqlCommandStatement =
string.Format("SELECT {0} FROM dbo.Users WHERE name=#name", "slot");
and then use the sp_executesql stored proc in SQL Server to execute that SQL command (and specify the other parameters as needed).
Dynamic SQL has its pros and cons - read the ultimate article on The Curse and Blessings of Dynamic SQL expertly written by SQL Server MVP Erland Sommarskog.

Categories

Resources