C# String Parameters to protect against injection? - c#

Take this code as an example:
IAmazonSimpleDB client = new AmazonSimpleDBClient(Amazon.RegionEndpoint.USEast1);
SelectResponse response = client.Select(new SelectRequest() {
SelectExpression = "SELECT * FROM `foo` where FooID = '" + id + "'" });
I can rewrite it as such:
IAmazonSimpleDB client = new AmazonSimpleDBClient(Amazon.RegionEndpoint.USEast1);
SelectResponse response = client.Select(new SelectRequest() {
SelectExpression = "SELECT * FROM `foo` where FooID = '{0}'", id });
But from my understanding, that still leaves it vulnerable to injection right?
Is there anything else I can do here? We aren't using SQL so I can't do SQL Parameters.

I usually do a check to see if the id is an integer. That way you will get an exception or a Boolean value if it isn't an int. It will work fine unless you are using GUID values.
var isNumeric = int.TryParse("123", out int n); //Will give a bool
Int32.Parse(yourString); //This will give an exception if it is not an possible integer
If it's anything more than that then you could use a Regex expression to look for strange values and remove characters that shouldn't be there such as spaces. Most SQL injection attacks wont work if there's no spaces... I think. Removing all the spaces is pretty easy and I would assume your ID (even if it is complex) won't include spaces.
string s = " "
string t = s.Replace(" ", ""). //It will be hard to do a sql attack if the spaces are removed.
A little off topic but with C# 6.0 you can format string differentlyl; It's a new feature called "string interpolation" (thanks Etienne de Martel).
$"SELECT * FROM `foo` where FooID = '{id}'"

Related

What is the correct way to mitigate SQL injection risk for a dynamic SQL lookup procedure with a variable number of parameters? [duplicate]

I'm using Dapper to work with sql database.
I have some search logic in my website project.
My search gets list of string parameters.
//filter is list of strings
var sql = new StringBuilder();
sql.Append("SELECT LibraryDocumentId FROM LibraryDocumentKeywords WHERE ");
sql.Append(string.Join("OR ", filter.Select(f => string.Format("LOWER(Keyword) LIKE '%{0}%'", f)).ToList()));
var isList = conn.Query<int>(sql.ToString()).ToList();
Actually I don't want to use this approach of generating dynamic SQL query, because Dapper will cache every single query. I would prefer to pass filter with parameter. Can someone help me with that? Any idea ?
What you have at the moment is also a huge SQL injection risk. You might want to use DynamicParameters here, i.e. (completely untested, you may need to tweak slightly):
var sql = new StringBuilder(
"SELECT LibraryDocumentId FROM LibraryDocumentKeywords");
int i = 0;
var args = new DynamicParameters();
foreach(var f in filter) {
sql.Append(i == 0 ? " WHERE " : " OR ")
.Append("LOWER(Keyword) LIKE #p").Append(i);
args.Add("p" + i, "%" + f + "%");
i++;
}
var data = conn.Query<int>(sql.ToString(), args);
This should cache fairly cleanly (one cache item per number of filters, regardless of their contents).

Build efficient SQL statements with multiple parameters in C#

I have a list of items with different ids which represent a SQL table's PK values.
Is there any way to build an efficient and safe statement?
Since now I've always prepared a string representing the statement and build it as I traversed the list via a foreach loop.
Here's an example of what I'm doing:
string update = "UPDATE table SET column = 0 WHERE";
foreach (Line l in list)
{
update += " id = " + l.Id + " OR";
}
// To remove last OR
update.Remove(update.Length - 3);
MySqlHelper.ExecuteNonQuery("myConnectionString", update);
Which feels very unsafe and looks very ugly.
Is there a better way for this?
So yeah, in SQL you've got the 'IN' keyword which allows you to specify a set of values.
This should accomplish what you would like (syntax might be iffy, but the idea is there)
var ids = string.Join(',', list.Select(x => x.Id))
string update = $"UPDATE table SET column = 0 WHERE id IN ({ids})";
MySqlHelper.ExecuteNonQuery("myConnectionString", update);
However, the way you're performing your SQL can be considered dangerous (you should be fine as this just looks like ids from a DB, who knows, better to be safe than sorry). Here you're passing parameters straight into your query string, which is a potential risk to SQL injection which is very dangerous. There are ways around this, and using the inbuilt .NET 'SqlCommand' object
https://www.w3schools.com/sql/sql_injection.asp
https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand?view=dotnet-plat-ext-6.0
It would be more efficient to use IN operator:
string update = "UPDATE table SET column = 0 WHERE id IN (";
foreach (Line l in list)
{
update += l.Id + ",";
}
// To remove last comma
update.Remove(update.Length - 1);
// To insert closing bracket
update += ")";
If using .NET Core Framework, see the following library which creates parameters for a WHERE IN. The library is a port from VB.NET which I wrote in Framework 4.7 years ago. Clone the repository, get SqlCoreUtilityLibrary project for creating statements.
Setup.
public void UpdateExample()
{
var identifiers = new List<int>() { 1, 3,20, 2, 45 };
var (actual, exposed) = DataOperations.UpdateExample(
"UPDATE table SET column = 0 WHERE id IN", identifiers);
Console.WriteLine(actual);
Console.WriteLine(exposed);
}
Just enough code to create the parameterizing SQL statement. Note ActualCommandText method is included for development, not for production as it reveals actual values for parameters.
public static (string actual, string exposed) UpdateExample(string commandText, List<int> identifiers)
{
using var cn = new SqlConnection() { ConnectionString = GetSqlConnection() };
using var cmd = new SqlCommand() { Connection = cn };
cmd.CommandText = SqlWhereInParamBuilder.BuildInClause(commandText + " ({0})", "p", identifiers);
cmd.AddParamsToCommand("p", identifiers);
return (cmd.CommandText, cmd.ActualCommandText());
}
For a real app all code would be done in the method above rather than returning the two strings.
Results
UPDATE table SET column = 0 WHERE id IN (#p0,#p1,#p2,#p3,#p4)
UPDATE table SET column = 0 WHERE id IN (1,3,20,2,45)

Is it safe method against sql injection?

Is it safe method for C# against sql injection?
string sqlDeclare = $"DECLARE #number nvarchar(MAX) SET #number = '%{sNumber}%' ";
string sqlFilter = "";
if (!string.IsNullOrEmpty(Number))
{
sqlFilter += $" and [table].[number] like #number ";
}
string sql = sqlDeclare + " SELECT [table].[*] ";
sql += " WHERE [table].[state] = 0 and [table].[column1] <> 3 AND [table].[id] > 0 ";
if (!string.IsNullOrEmpty(sqlFilter))
{
sql += sqlFilter;
}
sql += " order by datein";
P.S. I can't use Parametr.Add()
Is it safe method for C# against sql injection?
if sNumber actually was a numeric type, like int or decimal then I'd say "maybe" because it's pretty hard to inject SQL into a number, but at the end of the day, all you've done is change from concatenating a string value directly into an sql query, which is the classic fail:
"SELECT * FROM t WHERE x = " + str
into putting a string value into a db variable that you then use in an sql. It means the select query won't be the place where the injecting is done, but it just moves the injection point earlier:
Think what happens when sNumber is a value like '; DROP TABLE students;--
DECLARE #number nvarchar(MAX) SET #number = '%';
DROP TABLE Students;--%'
SELECT ...
If you truly are restricted to using this function (it needs throwing away, tbh) then you'll have to do your best at preventing injection by sanitizing the input. In this case you say you're expecting a document number like 2-12 so your C# should look like
if(!Regex.IsMatch(userInput, #"^\d+-\d+$"))
throw new ArgumentException("Bad input value, expecting document number like 2-12");
var dt = Execute4Table($"SELECT * FROM t WHERE x = '{input}');
In a general sense, it would be clunky, but you could perhaps do some wrapper function that takes N parameters and serializes them to JSON, then you pass them to sqlserver as a json string and get SQLS to deser them into N parameters that you use in the query. The two serializers should encode any user provided values in such a way that they are interpreted as data rather than code. You could also do a similar thing with base64 if your sqlserver is too old for json. To some extents this isn't much different to replacing all ' with '' but with a set/deser approach you're handing off responsibility for that encoding to a well tested library, which is probably the only thing you can do if you are rather hamstrung by this method you're forced to use
It would be safe if you type check the sNumber variable and you made sure it has no string dangerous data (your sNumber could have 0';TRUNCATE TABLE [table];-- and that would be SQL Injection). If you check if your snumber is integer, you would be safe on your string interpolation.
Otherwise it would not be safe. It is best to avoid string interpolation/string concatenation.
int checkedNumber;
if (Int32.TryParse(out checkedNumber, sNumber))
{
string sqlDeclare = $"DECLARE #number nvarchar(MAX) SET #number = '%{sNumber}%' ";
string sqlFilter = "";
if (!string.IsNullOrEmpty(Number))
{
sqlFilter += $" and [table].[number] like #number ";
}
string sql = sqlDeclare + " SELECT [table].[*] ";
sql += " WHERE [table].[state] = 0 and [table].[column1] <> 3 AND [table].[id] > 0 ";
if (!string.IsNullOrEmpty(sqlFilter))
{
sql += sqlFilter;
}
sql += " order by datein";
}

Column name in SQL query from request

I was given a task to rewrite an old web API.
This API reads SQL queries from the database.
There's literally a view with "Queries" in the name which contains "SqlText" column.
SELECT SqlText FROM Queries WHERE QueryID = 123
The "SqlText" contains only simple SQL queries in the format SELECT [columns] FROM [table] by convention.
The query is altered depending on the URL parameters in the request. The result of this query is then shown as result.
string parsedColumns = ParseColumns(queryRow); //contains "Column1, Column2";
string parsedTable = ParseTable(queryRow); //contains "SomeTable"
string requestColumns = HttpContext.Request["columns"];
string sqlColumns = requestColumns ?? parsedColumns;
string col1Condition = HttpContext.Request["Column1"]
string col2Condition = HttpContext.Request["Column2"]
string sqlQuery = "SELECT " + sqlColumns
+ " FROM " + parsedTable
+ " WHERE Column1 = " + col1Condition
+ " AND Column2 = " + col2Condition;
This is obvious SQL injection issue so I started rewritting it.
Now there are three other problems.
I cannot change the structure of the database or the convention
The database is either Oracle or SQL Server
I don't know how to correctly work with the "columns" URL parameter to avoid SQL injection.
It's easy to convert the URL parameters in the WHERE clause to the SQL parameters for both SQL Server and Oracle.
SQL Server
var sqlCommand = new SqlCommand("SELECT * FROM SomeTable WHERE Condition1 = #con1 AND Condition2 = #con2");
Oracle
var oracleCommand = new OracleCommand("SELECT * FROM SomeTable WHERE Condition1 = :con1 AND Condition2 = :con2");
Column identifiers
The problem is with the HttpContext.Request["columns"]. I still need to somehow alter the SQL query string with URL parameters which I don't like at all.
To simplify the issue, let's consider a single column from URL request.
string column = HttpContext.Request["column"];
var cmd = new SqlCommand($"SELECT {column} FROM ...");
I know that in SQL Server the identifier can be surrounded by braces. So my line of thinking is that I'm safe if I strip all braces from the column.
string column = HttpContext.Request["column"];
column = column.Replace("[", "").Replace("]", "");
column = $"[{column}]";
var cmd = new SqlCommand($"SELECT {column} FROM ...");
Oracle uses quotation marks.
string column = HttpContext.Request["column"];
column = column.Replace("\"", "");
column = $"\"{column}\"";
var cmd = new OracleCommand($"SELECT {column} FROM ...");
The question
Is this sql-injection safe enough?
Or is this use case inherently sql-injection unsafe?
Since you are working with a basic program design that you cannot change what about just trying to add edits to the input to look for injection elements. For example if the input is a column name it will need to have a maximum length of 30 (before 12.x) characters and should not contain a semicolon or the strings " OR" or " AND" in them. While not a perfect solution this should be practical solution.

dapper query - fix for "Unclosed quotation mark near ..." error

This query works; that is, it returns the expected results:
var r = sql.Query<T>("select * from TableName where Name = '" + name + "'");
but, if one of the 'names' values contains an apostrophy (which is true), then an exception is thrown '{"Incorrect syntax near 'Resources'.\r\nUnclosed quotation mark after the character string ''."}' - in an attempt to fix that problem, my query no longer returns any results; but it should.
I've attempted to change the code in a few ways, but no results are returned with either of the following changes:
var r = sql.Query<T>("select * from TableName where Name = '#name'", new { name });
or
var args = new DynamicParameters(name);
var r = sql.Query<T>("select * from TableName where Name = '#name'", args);
or
var args = new DynamicParameters(); args.AddDynamicParams(new { name });
var r = sql.Query<T>("select * from TableName where Name = '#name'", args);
or
var args = new DynamicParameters(); args.Add("#name", name);
var r = sql.Query<T>("select * from TableName where Name = '#name'", args);
This is probably something trivial that I have simply just not yet grasped the concept for ... but I'm at the point of having spent too much time trying to figure it out - hence the question.
Using a parameter is the right way to go. You absolutely don't want to put the value into the query itself as you did in your first snippet. However, you've put the #name in quotes, which means it's being treated as a string literal... it's looking for a name value of exactly #name rather than the value of the #name parameter. You want:
var r = sql.Query<T>("select * from TableName where Name = #name", new { name });
(That's probably the simplest way of passing the parameters, though the other approaches should work too.)
Now I haven't actually used Dapper myself, but that's what I'd expect...

Categories

Resources