Escaped interpolated strings in C# - c#

I find it unnecessarily cumbersome to create prepared statements in C# generally what you do is something like this:
public T GetData<T>(string userInput)
{
string selectSomething = "SELECT * FROM someTable where someCol = #userInput";
using (IDbCommand command = new SqlCommand(selectSomething))
{
IDbDataParameter parameter = new SqlParameter("#userInput", SqlDbType.NVarChar);
parameter.Value = userInput;
command.Parameters.Add(parameter);
IDataReader reader = command.ExecuteReader();
reader.Read();
}
...
}
But now as there are interpolated strings it could be as easy like this:
public T GetData<T>(string userInput)
{
string selectSomething = $"SELECT * FROM someTable where someCol = {userInput}";
using (IDbCommand command = new SqlCommand(selectSomething))
{
IDataReader reader = command.ExecuteReader();
reader.Read();
}
}
There's still some other boilerplate code but still an improvement. Is there maybe a way to get the comfort of interpolated strings but still keep the safety of prepared statements with something like this:
string selectSomething = $"SELECT * FROM someTable where someCol = {userInput.PreventSQLInjections()}";

If you don't want to use EF which has FromSqlInterpolated method or any other ORM which will help you to handle your data access you can leverage fact that compiler uses FormattableString type to handle string interpolation to write helper method looking something like this (not fully working, but you should get the idea) to remove the boilerplate code:
public static class SqlCommandEx
{
// maps CLR type to SqlDbType
private static Dictionary<Type, SqlDbType> typeMap;
static SqlCommandEx()
{
typeMap = new Dictionary<Type, SqlDbType>();
typeMap[typeof(string)] = SqlDbType.NVarChar;
//... all other type maps
}
public static SqlCommand FromInterpolatedString(FormattableString sql)
{
var cmdText = sql.Format;
int count = 0;
var #params = new IDbDataParameter[sql.ArgumentCount];
foreach (var argument in sql.GetArguments())
{
var paramName = $"#param_{count}";
cmdText = cmdText.Replace($"{{{count}}}", paramName);
IDbDataParameter parameter = new SqlParameter(paramName, typeMap[argument.GetType()]);
parameter.Value = argument;
#params[count] = parameter;
count++;
}
var sqlCommand = new SqlCommand(cmdText);
sqlCommand.Parameters.AddRange(#params);
return sqlCommand;
}
}
And usage:
using (IDbCommand command = SqlCommandEx.FromInterpolatedString($"Select * from table where id = {val}"))
{
...
}
But this comes close to writing your own ORM which you usually should not do.

Prepared statements/parameterized queries go further than just sanitizing or escaping the inputs. When you use parameterized queries, the parameter data are sent as separate values from the SQL statement. The parameter data is never substituted directly into the SQL, and therefore injection is perfectly protected in a way that escaping/sanitizing the input never will.
In other words, DON'T COUNT ON STRING INTERPOLATION FOR "FIXING" SQL PARAMETERS!
Moreover, it's really not that much extra work. What the question shows is the hard way for adding parameters. You can simplify that code like this:
public T GetData<T>(string userInput)
{
string selectSomething = "SELECT * FROM someTable where someCol = #userInput";
using (IDbCommand command = new SqlCommand(selectSomething))
{
command.Parameters.Add("#userInput", SqlDbType.NVarChar).Value = userInput;
IDataReader reader = command.ExecuteReader();
reader.Read();
}
...
}
This gets the extra work for parameters down to one line of code per parameter.
If you have high-confidence in the mapping between C# types and SQL types, you can simplify even further like this:
command.Parameters.AddWithValue("#userInput", userInput);
Just be careful with that shortcut: if ADO.Net guesses the SQL data type wrong, it can break indexing and force per-row type conversions, which can really kill performance.

Related

C# getAll Function advice

Hi I am trying to create CRUD functions in C# but am stuck on my first one which is FetchALL, as so far it says not all code path returns a value.
Heres my code so far
public SqlDataReader FetchAll(string tableName)
{
using (SqlConnection conn = new SqlConnection(_ConnectionString,))
{
string query = "SELECT * FROM " + tableName;
SqlCommand command = new SqlCommand(query, conn);
using (SqlDataReader reader = command.ExecuteReader())
conn.Open();
conn.Close();
}
}
}
}
I can give you more information, thanks
You have a return type of SqlDataReader, but you aren't returning anything anywhere in your code. At the very least you should declare your data reader and return it like this:
public SqlDataReader FetchAll(string tableName)
{
SqlDataReader reader;
using (SqlConnection conn = new SqlConnection(_ConnectionString))
{
string query = "SELECT * FROM " + tableName;
// added using block for your command (thanks for pointing that out Alex K.)
using (SqlCommand command = new SqlCommand(query, conn))
{
conn.Open(); // <-- moved this ABOVE the execute line.
reader = command.ExecuteReader(); // <-- using the reader declared above.
//conn.Close(); <-- not needed. using block handles this for you.
}
}
return reader;
}
Note, I've noted a few other problems I saw as well, which you can see by my comments.
Also, I want to point out something very important: you should always avoid string concatenation in queries as this opens you up to the risk of a SQL injection attack (as gmiley has duly pointed out). In this case, you should create an enum which contains values associated with all the possible table names, and then use a dictionary to look up the table names based on their enum values. If a user provides an invalid/unknown value, you would then thrown an argument exception.
This isn't the end of your problems, though (as Default has pointed out). You can't create the connection in a using block, which disposes and closes as soon as it exits the block, and then use the SqlDataReader that is returned from the method. If I were you, I'd return a DataSet instead of a SqlDataReader. Here's how I'd do it:
First, create your enum of possible table values:
public enum Table
{
FirstTable,
SecondTable
}
And a dictionary that maps table enum values to the table names (which you will populate in your static constructor):
private static Dictionary<Table, string> _tableNames = new Dictionary<Table, string>(); // populate this in your static constructor.
And then here is your method to fetch the data:
public static System.Data.DataSet FetchAll(Table fromTable)
{
var ret = new System.Data.DataSet();
using (var conn = new System.Data.SqlClient.SqlConnection(_connectionString))
{
string tableName = "";
if (!_tableNames.TryGetValue(fromTable, out tableName)) throw new ArgumentException(string.Format(#"The table value ""{0}"" is not known.", fromTable.ToString()));
string query = string.Format("SELECT * FROM {0}", tableName);
using (var command = new System.Data.SqlClient.SqlCommand(query, conn))
{
using (var adapter = new System.Data.SqlClient.SqlDataAdapter(command))
{
adapter.Fill(ret);
}
}
}
return ret;
}
One final note, I'd advise you name your class-level variables with lower camel case per convention, e.g. _connectionString.
Firstly you aren't returning anything from the method. I'd add, are you sure you want to return a SqlDataReader? It is declared within a using block, so it will be closed by the time you return it anyway. I think you should re-evaluate what this function should return.
You need a return statment for the method to return a value.

select query does not work with parameters using Parameters.AddWithValue

The following query in C# doesn't work, but I can't see the problem:
string Getquery = "select * from user_tbl where emp_id=#emp_id and birthdate=#birthdate";
cmdR.Parameters.AddWithValue("#emp_id", userValidate.emp_id);
cmdR.Parameters.AddWithValue("#birthdate", userValidate.birthdate);
OdbcCommand cmdR = new OdbcCommand(Getquery, conn);
OdbcDataReader Reader = cmdR.ExecuteReader();
Reader.HasRows returns no result but when I query it to my database I got data.
I'll assume your code is actually not quite as presented, given that it wouldn't currently compile - you're using cmdR before you declare it.
First, you're trying to use named parameters, and according to the documentation of OdbcCommand.Parameters, that isn't supported:
When CommandType is set to Text, the .NET Framework Data Provider for ODBC does not support passing named parameters to an SQL statement or to a stored procedure called by an OdbcCommand. In either of these cases, use the question mark (?) placeholder.
Additionally, I would personally avoid using AddWithValue anyway - I would use something like:
string sql = "select * from user_tbl where emp_id = ? and birthdate = ?";
using (var connection = new OdbcConnection(...))
{
connection.Open();
using (var command = new OdbcCommand(sql, connection))
{
command.Parameters.Add("#emp_id", OdbcType.Int).Value = userValidate.EmployeeId;
command.Parameters.Add("#birthdate", OdbcType.Date).Value = userValidate.BirthDate;
using (var reader = command.ExecuteReader())
{
// Use the reader here
}
}
}
This example uses names following .NET naming conventions, and demonstrates properly disposing of resources... as well as fixing the parameter issue.
I do think it's slightly unfortunate that you have to provide a name for the parameter when adding it to the command even though you can't use it in the query, but such is life.
Use like this:
string Getquery = "select * from user_tbl where emp_id=? and birthdate=?";
cmdR.Parameters.AddWithValue("#emp_id", userValidate.emp_id);
cmdR.Parameters.AddWithValue("#birthdate", userValidate.birthdate);
OdbcCommand cmdR = new OdbcCommand(Getquery, conn);
OdbcDataReader Reader = cmdR.ExecuteReader();
while(Reader.Read())
{
//Do something;
}
I know this thread is old, but I wanted to share my solution for anyone else coming up on this.
I was having issues with the typical method that Jon posted. I have used it before, but for some reason with this new string I had it was not wanting to actually place the parameter correctly and was causing the reader to not work.
I ended up doing something like this instead, since in the end we are just replacing parts of a string.
string sql = "select * from user_tbl where emp_id = "+ var1 +" and birthdate = "+
var2""
OdbcCommand command = new OdbcCommand(sql);
This was easier for me to get to work. Be warned though, I am not sure if it has any specific drawbacks when compare to using the command parameter method.

parameterized sql query - asp.net / c#

So I recently learned that I should absolutely be using parametrized query's to avoid security issues such as SQL injection. That's all fine and all, I got it working.
This code shows some of the code how I do it:
param1 = new SqlParameter();
param1.ParameterName = "#username";
param1.Value = username.Text;
cmd = new SqlCommand(str, sqlConn);
cmd.Parameters.Add(param1);
//and so on
But the problem is, I have over 14 variables that needs to be saved to the db, it's like a registration form. And it would look really messy if I have to write those lines 14 times to parametrize each variable. Is there a more dynamic way of doing this? Like using a for loop or something and parametrizing every variable in the loop somehow?
Use single line SqlParameterCollection.AddWithValue Method
cmd.Parameters.AddWithValue("#username",username.Text);
or other variation you might try like this
command.Parameters.Add(new SqlParameter("Name", dogName));
Here you go... via dapper:
connextion.Execute(sql, new {
username = username.Text,
id = 123, // theses are all invented, obviously
foo = "abc",
when = DateTime.UtcNow
});
that maps to ExecuteNonQuery, but there are other methods, such as Query<T> (binds the data very efficiently by name into objects of type T per row), Query (like Query<T>, but uses dynamic), and a few others (binding multiple grids or multiple objects, etc). All ridiculously optimized (IL-level meta-programming) to be as fast as possible.
Another technique, you can use..
List<SqlParameter> lstPrm = new List<SqlParameter>();
lstPrm.Add(new SqlParameter("#pusername", usernameValue ));
lstPrm.Add(new SqlParameter("#pID", someidValue));
lstPrm.Add(new SqlParameter("#pPassword", passwordValue));
Add the end you can iterate to insert the parameters in your command object
Use my SqlBuilder class. It lets you write paramaterized queries without ever creating a parameter, or having to worry about what its called. Your code will look like this...
var bldr = new SqlBuilder( myCommand );
bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId);
//or
bldr.Append("SELECT * FROM CUSTOMERS NAME LIKE ").FuzzyValue(myName);
myCommand.CommandText = bldr.ToString();
Your code will be shorter and much more readable. Compared to concatenated queries, you don't even need extra lines. The class you need is here...
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
public class SqlBuilder
{
private StringBuilder _rq;
private SqlCommand _cmd;
private int _seq;
public SqlBuilder(SqlCommand cmd)
{
_rq = new StringBuilder();
_cmd = cmd;
_seq = 0;
}
public SqlBuilder Append(String str)
{
_rq.Append(str);
return this;
}
public SqlBuilder Value(Object value)
{
string paramName = "#SqlBuilderParam" + _seq++;
_rq.Append(paramName);
_cmd.Parameters.AddWithValue(paramName, value);
return this;
}
public SqlBuilder FuzzyValue(Object value)
{
string paramName = "#SqlBuilderParam" + _seq++;
_rq.Append("'%' + " + paramName + " + '%'");
_cmd.Parameters.AddWithValue(paramName, value);
return this;
}
public override string ToString()
{
return _rq.ToString();
}
}
Better still, use my shiny new Visual Studio extension. You declare your parameters in your sql, intact in its own file. My extension will run your query when you save your file, and will make you a wrapper class to call at runtime, and a results class to access your results, with intellisense all over da place. You will see your sql parameters as arguments to the Execute() methods of the wrapper class. You will never have to write another line of parameter code in C#, or reader code, or cmd, or even connection (unless you want to manage that yourself). Gone gone gone :-)

Whatis is best approach on executing multiple UPDATE statement in one connection?

How to properly do the following update:
using (OracleConnection conn = new OracleConnection())
using (selCmd)
{
string sql1 = "update Table1 set name = joe where id = 10;"
string sql2 = "update Table2 set country = usa where region = americas;"
string sql3 = "update Table3 set weather = sunny where state = CA;"
string sql4 = "update Table4 set engine = v8 where maker = benz;"
cmdUpdate.CommandText = sql(#);
cmdUpdate.Connection = conn;
recs = cmdUpdate.ExecuteNonQuery();
}
I am aware of all or nothing if it's a transaction but I just to see how it works with correct approach.
I'm thinking iterate an array of items [sql1,sql2,sql3,sql4] and pass sql(#) in the CommandText and perform ExecuteNonQuery each time.
If I remember correctly, it is possible to concatenate multiple SQL statements in one string separated by semi-colons (;). Otherwise, there is nothing wrong with executing multiple ExecuteNonQuery() calls.
string sql1 = "BEGIN update Table1 set name = 'joe' where id = 10;",
sql2 = "update Table2 set country = 'usa' where region = 'americas';",
sql3 = "update Table3 set weather = 'sunny' where state = 'CA';",
sql4 = "update Table4 set engine = 'v8' where maker = 'benz'; END;";
string sql = string.Format("{0}{1}{2}{3}",sql1,sql2,sql3,sql4);
using (OracleConnection conn = new OracleConnection())
using (OracleCommand cmdUpdate = new OracleCommand(sql, conn))
{
conn.Open();
recs = cmdUpdate.ExecuteNonQuery();
}
I recently came across this issue in some old code. We dynamically build chain of SQL calls (with support for Oracle and Sql Server). Since there is no current Oracle production implementation, nobody tested Oracle operation and customer bugs are not coming in. I found a code that builds chain of commands and then, for Oracle it uses String.Split(';'). Then, it uses a loop to execute each statement in transaction: rowsAffecter += ExecuteNonQuery....
I don't like this idea because without parameterization it is dangerous approach, since some data can contain ;. But even if parameterization is in place...
... one of the issues of making anonymous block for Oracle ("begin... end;") is that ExecuteNonQuery will not return number of rows (returns -1), which is sometimes needed to judge if something got updated or not.
to solve this issue I've done this
private string AppendOracleCountOrNothing(StringBuilder sql)
{
if (_myProvider == Providers.Oracle)
sql.AppendLine("rowCnt := rowCnt + SQL%ROWCOUNT;");
}
public void SomeMethod()
{
var longSqlChain = new StringBuilder(2000);
longSqlChain.Append("Insert into table...;");
AppendOracleCountOrNothing(longSqlChain);
if (someCondition)
{
longSqlChain.AppendLine("Update anotherTable...;");
AppendOracleCountOrNothing(longSqlChain);
}
// may be, add some more sql to longSqlChain here....
int rowsAffected;
if (_myProvider == Providers.Oracle)
{
longSqlChain.Insert(0, #"DECLARE
rowCnt number(10) := 0
BEGIN
").AppendLine(#":1 := rowCnt;
END;");
// Now, here we have some abstract wrappers that hide provider specific code.
// But the idea is to prepare provider specific output parameter and then parse its value
IDataParameter p = ParameterWrapper.PrepareParameter(":1", 0, ParameterDirection.Output, myProvider); // note IDataParameter
SqlExecWrapper.ExecuteNonQuery(_myProvider, CommandType.Text, sql, new[]{p});
rowsAffected = p.GetParameterValue(); // GetParameterValue is an extension on IDataParameter
}
else // sql server
{
rowsAffected = SqlExecWrapper.ExecuteNonQuery(_myProvider, CommandType.Text, sql, null);
}
}
This way we make one trip to DB and get the return number of rows affected by this call. and queries can be parameterized as well. Again, better to develop abstraction layer, so, you can call something like parameterizer.CreateParameter(10), which will add parameter to collection and generate :1, :2, :3, etc. (oracle) and #1, #2, #3, etc. (sql server), in your sql statement.
Another approach is to create a simple extension method (ExecuteMultipleNonQuery) that simply splits the string on all semicolons and executes each statement in a loop:
public static class DbCommandExtensions {
public static void ExecuteMultipleNonQuery(this IDbCommand dbCommand)
{
var sqlStatementArray = dbCommand.CommandText.Split(new string[] {";"}, StringSplitOptions.RemoveEmptyEntries);
foreach (string sqlStatement in sqlStatementArray)
{
dbCommand.CommandText = sqlStatement;
dbCommand.ExecuteNonQuery();
}
}
}

How to prevent a SQL Injection escaping strings

I have some queries (to an acccess database) like this :
string comando = "SELECT * FROM ANAGRAFICA WHERE E_MAIL='" + user + "' AND PASSWORD_AZIENDA='" + password + "'";
and I'd like to "escape" user and password, preventing an injection.
How can I do it with C# and .NET 3.5? I'm searching somethings like mysql_escape_string on PHP...
You need to use parameters. Well dont have to but would be preferable.
SqlParameter[] myparm = new SqlParameter[2];
myparm[0] = new SqlParameter("#User",user);
myparm[1] = new SqlParameter("#Pass",password);
string comando = "SELECT * FROM ANAGRAFICA WHERE E_MAIL=#User AND PASSWORD_AZIENDA=#Pass";
Don't escape the strings to start with - use a parameterized query. Benefits of this over escaping:
The code is easier to read
You don't have to rely on getting the escaping correct
It's possible that there are performance improvements (DB-specific etc)
It separates "code" (the SQL) from the data, which is just good sense logically
It means you don't need to worry about data formats for things like numbers and dates/times.
The docs for SqlCommand.Parameters give a good, complete example.
You should use the SQL paramters to prevent SQL Injection
look at the code
//
// The name we are trying to match.
//
string dogName = "Fido";
//
// Use preset string for connection and open it.
//
string connectionString = ConsoleApplication716.Properties.Settings.Default.ConnectionString;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
//
// Description of SQL command:
// 1. It selects all cells from rows matching the name.
// 2. It uses LIKE operator because Name is a Text field.
// 3. #Name must be added as a new SqlParameter.
//
using (SqlCommand command = new SqlCommand("SELECT * FROM Dogs1 WHERE Name LIKE #Name", connection))
{
//
// Add new SqlParameter to the command.
//
command.Parameters.Add(new SqlParameter("Name", dogName));
//
// Read in the SELECT results.
//
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
int weight = reader.GetInt32(0);
string name = reader.GetString(1);
string breed = reader.GetString(2);
Console.WriteLine("Weight = {0}, Name = {1}, Breed = {2}", weight, name, breed);
}
}
}
Yes, you can avoid injection by using Named Parameters
Use parameters instead of escaping strings:
var comando = "SELECT * FROM ANAGRAFICA WHERE E_MAIL=#user AND PASSWORD_AZIENDA=#password";
Then assign values to those parameters before you execute the SqlCommand.
You can check the below link to know how to prevent SQL injection in ASP.Net. I would prefer to use
Using parametrized queries or Stored Procedures.
Validating special characters like '(very dangerous)
http://dotnet.dzone.com/news/aspnet-preventing-sql-injectio
If you can convert these to Named Parameters, I think you would be better served.
#Jethro
You could also write it like this:
SqlParameter[] sqlParams = new SqlParameter[] {
new SqlParameter("#Name", contact.name),
new SqlParameter("#Number", contact.number),
new SqlParameter("#PhotoPath", contact.photoPath),
new SqlParameter("#ID", contact.id)
};
Follow the steps below and resolve the SQL INJECTION problem:
OracleParameter[] tmpParans = new OracleParameter[1];
tmpParans[0] = new Oracle.DataAccess.Client.OracleParameter("#User", txtUser.Text);
string tmpQuery = "SELECT COD_USER, PASS FROM TB_USERS WHERE COD_USER = #User";
OracleCommand tmpComand = new OracleCommand(tmpQuery, yourConnection);
tmpComand.Parameters.AddRange(tmpParans);
OracleDataReader tmpResult = tmpComand.ExecuteReader(CommandBehavior.SingleRow);

Categories

Resources