Instead of using ? marks within an SQL statement for parameters, I'd like to use named fields as it's more readable than WHERE something = ? AND somethingelse < ? and whatever LIKE ?
Is the following possible in some capacity?
SELECT * FROM NAMESPACE.Table WHERE Name = #name AND Something = #anothervar
The official documentation doesn't mention it at all and shows me the following
string SQLtext =
"SELECT ID, Name, DOB, SSN "
+ "FROM Sample.Person "
+ "WHERE Name %STARTSWITH ?"
+ "ORDER BY Name";
CacheCommand Command = new CacheCommand(SQLtext, CacheConnect);
The parameter value is set to get all rows where Name starts with A, and the parameter is passed to the CacheCommand object:
CacheParameter Name_param =
new CacheParameter("Name_col", CacheDbType.NVarChar);
Name_param.Value = "A";
Command.Parameters.Add(Name_param);
Named SQL parameters in Caché available only in two ways.
Embedded sql with &sql(). You can't use this way, while are working from .Net, just because it is CacheObjectScript only.
Class Query You can't also use it, just because such SQL queries should be defined on Class itself, and when you call it, you just should pass all arguments, without names.
So, in your case no way.
Related
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.
This question already has answers here:
How to pass a table as parameter to MySqlCommand?
(2 answers)
Closed 6 years ago.
I am working in C# and MySQl in VS2015 to query my database and return a the information in a VARCHAR type column titled "method". However, the query returns the string "method", and not the values of the method column.
below is the code:
string queryOne = "SELECT " + "#columnName" + " FROM log.transactions";
MySqlCommand cmdOne = new MySqlCommand(queryOne, connectionString);
cmdOne.Parameters.AddWithValue("#columnName", "method");
MySqlDataReader dataReaderOne = cmdOne.ExecuteReader();
while (dataReaderOne.Read())
{
Console.WriteLine(dataReaderOne.GetString(0));
}
dataReaderOne.Close();
While this is the output:
method
method
method
.
.
.
.. for the number of rows in the method column. Is this a formatting problem? Is it possible that the configuration of my database is preventing VarChar's from returning correctly? When I change the query to query a column of type INT, it returns the correct values for an INT type column.
You can't parameterize a column name in a select statment. What you're doing is exaclty like saying select 'foo' from log.transactions. It selects the string 'foo' once for each row. You're just sticking a string value in there; it's not parsing the string value as SQL.
What you can do (if you can afford it) is select * from log.transactions, then your C# code can grab the data in whatever column the caller passed you the name of. With a lot of rows you could be dragging a lot of useless junk back from the DB though.
What you want in the code you show, though is just this:
string queryOne = "SELECT method FROM log.transactions";
If you really want to parameterize "method", that's sketchy because of SQL injection vulnerabilities.
string queryOne = "SELECT " + fieldname + " FROM log.transactions";
That looks good until some comedian using your application gives you a value of "0; drop table log.transactions;--" in the textbox. Then you've got troubles. If you ever concatenate a string variable into a SQL string that you're going to execute, you've got to be fanatical about sanitizing it, and even then you want to avoid it any way you can. It's Russian roulette.
Your query formation has to be like if you want to keep your column dynamic.Now pass column name accordingly.
string queryOne = "SELECT " + column_name + " FROM log.transactions";
MySqlCommand cmdOne = new MySqlCommand(queryOne, connectionString);
MySqlDataReader dataReaderOne = cmdOne.ExecuteReader();
while (dataReaderOne.Read())
{
Console.WriteLine(dataReaderOne[column_name]);
}
dataReaderOne.Close();
Two small issues, mostly concerning the #AT syntax when dealing with data in ASP.Net (C#). Most online tutorials show a lot of this following type of code but fail to mention (or I may have overlooked) the actual purpose of the ampersand although they do explain the general purpose of the code. In this example, just querying the database to get data pertaining to a certain month for a calender control.
protected DataSet GetCurrentMonthData(DateTime firstDate,
DateTime lastDate)
{
DataSet dsMonth = new DataSet();
ConnectionStringSettings cs;
cs = ConfigurationManager.ConnectionStrings["ConnectionString1"];
String connString = cs.ConnectionString;
SqlConnection dbConnection = new SqlConnection(connString);
String query;
query = "SELECT HolidayDate FROM Holidays " + _
" WHERE HolidayDate >= #firstDate AND HolidayDate < #lastDate";
SqlCommand dbCommand = new SqlCommand(query, dbConnection);
dbCommand.Parameters.Add(new SqlParameter("#firstDate",
firstDate));
dbCommand.Parameters.Add(new SqlParameter("#lastDate", lastDate));
SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(dbCommand);
try
{
sqlDataAdapter.Fill(dsMonth);
}
catch {}
return dsMonth;
}
First issue: What do #firstDate and #lastDate mean or reference in the database string query? Is that referencing the parameters being passed in GetCurrentMonthData method or the actual column name in the database table ?
query = "SELECT HolidayDate FROM Holidays " + _
" WHERE HolidayDate >= #firstDate AND HolidayDate < #lastDate";
Second issue: What is the difference between #firstDate and firstDate? Am I correct in saying firstDate is the actual parameter variable itself?
dbCommand.Parameters.Add(new SqlParameter("#firstDate",
firstDate));
I agree with #har07. That is not a ampersand. An ampersand, as far as I know, looks like this -> &. To answer the question, the 'at' sign (#) is used to indicate variables in a parameterized query in c#.
In the code
dbCommand.Parameters.Add(new SqlParameter("#firstDate",firstDate));
you are assigning the value of the DateTime variable firstDate to the #firstDate variable in your query.
Here's an example that's a bit less confusing(I hope):
Let's say I have a string variable called myName and I want to pass that to my query select * from students where name = #name.
To pass the value of myName to #name in my query, I would do
dbCommand.Parameters.Add(new SqlParameter("#name",myName));
I hope that helps.
Your First Question :
According to the documentation, the name must start with an #:
The ParameterName is specified in the form #paramname.
More Information :
Is it necessary to add a # in front of an SqlParameter name?
Second Question :
First, that isn't ampersand but at sign or commercial at. It is used in this particular context to indicates an SQL parameter name.
And this part showing how you pass the actual value (contained in the firstDate C# variable) to the SQL parameter (#firstDate) :
dbCommand.Parameters.Add(new SqlParameter("#firstDate",
firstDate));
You can read your parameterized SQL query statement like string concatenation but with big advantages (the former save you from SQL injection, arbitrary data type to string conversion with correct formatting*, etc) :
query = "SELECT HolidayDate FROM Holidays " + _
" WHERE HolidayDate >= " + firstDate + " AND HolidayDate < " + lastDate;
*) See that in the string concatenation version above you need to convert firstDate and lastDate to string with correct format according to your RDBMS local settings to make it work.
The main use of #inputvalue in query statement is to avoid sql injection attacks.
If you use normal concatenation method in building query statement, Hackers can easily bypass the statements with sql injection.
Eg:
"Select * from user where username ='" + username.text + "' and password ='" + password.text + "'"
If you use the above statement to validate the user login, think what will happen if the user types a' = 'a') or 'sometext in username textbox and sometext in password box. It will returns more than one record on execution and enters into account area, if you checks with no of return records in user validation.
To avoid this, Microsoft introduced #parameter to build sql query statements. Whatever value you pass in the #parameter is considered as input parameter value and you can't inject sql statements in it.
Answer for your second question
dbCommand.Parameters.Add(new SqlParameter("#firstDate",
firstDate));
The above method is used to replace the #parameter(first argument in Add method) with parameter value(second argument in Add method). It uses # as the delimiter. it checks if there is a word with prefix '#' in query statement, it will be marked as a parameter and it is replaced by the actual value which is passed as the second argument in the Add method.
I have a class, ValuesField, which manages values for ComboBoxes.
The constructor specifies a database table from which the values can be retrieved, along with two fields from which to SELECT data.
public ValuesField(string databaseTable, string idField, string valueField)
{
this.databaseTable = databaseTable;
this.idField = idField;
this.valueField = valueField;
}
My method, GetValues() retrieves this data (from which a ComboBox can then be populated). I was constructing the CommandText as a simple string, however I wanted to use a parameterized query for safety.
The simple string command -
dbCommand.CommandText = "SELECT " + idField + "," + valueField + " FROM " +
databaseTable + " ORDER BY " + valueField;
The parameterized query -
dbCommand.CommandText = "SELECT #idField, #valueField FROM #databaseTable
ORDER BY #valueField";
dbCommand.Parameters.AddWithValue("#idField", idField);
dbCommand.Parameters.AddWithValue("#valueField", valueField);
dbCommand.Parameters.AddWithValue("#databaseTable", databaseTable);
dbReader = dbCommand.ExecuteReader();
The parameterized query throws a MySqlException on ExecuteReader() with the Message 'You have an error in your SQL syntax'.
I checked the value of the CommandText at the point the Exception is thrown and it (using the Watch) and the CommandText still shows as "SELECT #idField, #valueField FROM #databaseTable ORDER BY #valueField" - so I am not sure how to examine the syntax for any obvious errors as I would normally do.
I see that this is apparently not possible according to this answer.
Is there a way to view the actual CommandText with the values included in order to diagnose syntax errors?
Is there a safe alternative to specify a table name, if indeed using a parameterized query is not possible?
Try creating a generic table for your combo boxes: [id, value, combo] and a add the combo metadata to other tables.
Or just use a repository pattern :)
I've got the following query that returns 2 records (in DataSet's query builder)
SELECT EmpID, Name, id
FROM Users
WHERE (CAST(id AS Varchar(20)) IN ('5688','5689'))
Now if I do the same query passing the parameter instead from code behind: String param = "'5688','5689'"; it returns null.
WHERE (CAST(id AS Varchar(20)) IN (#param))
I tried taking off the very first and last ', but that did not make a diffrence.
!!!id is a unique PK!!!
Anyone's got a clue?
The solution I found is quite simple, this works like a charm and there's no need for sps or other functions;
SQL:
SELECT whatever
FROM whatever
WHERE (PATINDEX('%''' + CAST(id AS Varchar(20)) + '''%', #param) > 0)
C#:
String param = "'''1234'',''4567'''";
dataTable1 = tableAdapter1.getYourValues(param);
A variable is not allowed in the IN clause.
You are expecting the values as a comma delimited string you could use the split function (user defined and non-standard) to join them with the original tables:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=326300&SiteID=1
For more information you can visit this
('5688','5689') is an array of values.
Defining String param = "'5688','5689'"; and using it as (#param) makes ('5688','5689') a string. Which wont work.
Bibhas is correct. For me this worked:
string param="'1234','4567'"; we can't use param as SQL Parameter(#param).
command = new SqlCommand("SELECT * FROM table WHERE number IN (" + param + ")", connection);
command.ExcecuteReader();