How do I add parameters to my queries when using clr/c++ for multiple variables - c++-cli

I want to be able to add parameters to my queries to make my statements less vunerable to sql injections
My code (Key parts are surrounded by ** as I cannot make code bold)
OleDbConnection^ existingSqlConnection = nullptr;
existingSqlConnection = gcnew OleDbConnection("Provider = Microsoft.ACE.OLEDB.12.0;" +
"Data Source =" + "myDatabaseV3.accdb");
**String^ sqlText = "SELECT * FROM #tableName WHERE #fieldName = #fieldEntityName";
OleDbCommand^ dbCommand = gcnew OleDbCommand(sqlText, existingSqlConnection);
OleDbParameterCollection^ paramCollection = dbCommand->Parameters;
dbCommand->Parameters->Add(gcnew OleDbParameter("#tableName", tableName->ToString()));
dbCommand->Parameters->AddWithValue("#fieldName", field);**
dbCommand->Parameters->AddWithValue("#fieldEntityName", fieldEntity);
**Console::WriteLine(dbCommand->CommandText);
Console::WriteLine(paramCollection->Count);**
existingSqlConnection->Open();
OleDbDataReader^ reader = dbCommand->ExecuteReader(System::Data::CommandBehavior::CloseConnection);
return reader;
The output for this is
SELECT * FROM #tableName WHERE #fieldName = #fieldEntityName
3
Which clearly shows that there are 3 parameters but they are not being added to my query which is the problem I want to be solved

This is not how parameters work. They are not replacement values for text templates. Parameters are a mechanism to pass in a value to the variable in the query, just like passing a parameter to a stored procedure will pass that value in to be used as a variable in the query.
Along these lines, parameters can only be used where variables can be used in queries. And, variables cannot be used for object names, which is why people resort to using SQL Injection when the table and/or column name(s) need to change. In your example code:
#tableName could never be a variable in a query, hence it cannot be a parameter
#fieldName could never be a variable in a query, hence it cannot be a parameter
#fieldEntityName if it is supposed to be a value and not a column name, can be a parameter, in which case it will remain as #fieldEntityName in the query, and it will have a value of fieldEntity.
Please see the second option of my answer on your related question for how to prevent SQL Injection (short answer: sanitize inputs).
Also, using AddWithValue() is a bad practice. Create the parameter with the intended max size, then give that a value, and finally add it to the parameters collection. You do not want it to auto-detect the max size for the parameter as it will use the first value it gets, and any subsequent value that is longer will be silently truncated.

Related

Error: assigning a variable output to other variable in snowflake procedure

In one procedure I am calling other procedure where the result is assigned to the P_DATALAKEPATH variable and the ouput of internal procedure is READ|WRITE|UPDATE and I am trying to split the value and assign to the MY_VAR variable which is giving an getting error. Please help
var p_sqlText = `CALL LS_${P_ENV}.dbo.dbo(:1,:2)`
stmt = snowflake.createStatement({sqlText:p_sqlText,binds: ['1','2'] });
rs = stmt.execute();
rs.next();
var P_DATALAKEPATH = rs.getColumnValue(1);
set MY_VAR=SPLIT_PART(P_DATALAKEPATH,'|',1);
If the number of output variables is predictable, I suggest to not put multiple values in a single string and separate them by parsing the string in JS like you're trying to do.
Since your SQL generates a table, instead of putting everything in one column with a pipe separator, why not creating several columns in the output and get them back to your SP with rs.getColumnValue(1), rs.getColumnValue(2), rs.getColumnValue(3) etc?
You are mixing JavaScript and SQL in your code. This is SQL:
set MY_VAR=SPLIT_PART(P_DATALAKEPATH,'|',1);
You need to use JavaScript to split the value of P_DATALAKEPATH, and the get the first value:
var MY_VAR = P_DATALAKEPATH.split("|")[0];

How do I prevent my SQL statements from SQL injection when using CLR/C++ with multiple variables?

I am having a major problem where I do not know how to prevent SQL injection when writing SQL statements in CLR/C++
Below is the code
String^ sqlstr = "SELECT * FROM ";
sqlstr += tableName + " WHERE " + field + " = " + fieldEntity;
I need to be able to input correct SQL Injection preventions to this statement.
Background code
class database
{
protected:
string fieldEntity;
string tableName;
string field;
...
____
OleDbDataReader^ openData(String^ fieldEntity, String^ field, String^ tableName)
{
String^ sqlstr = "SELECT * FROM ";
sqlstr += tableName + " WHERE " + field + " = " + fieldEntity;
...
___
OleDbDataReader^ reader2 = testData.openData(effectID, "effectID", "effectOnUser");
while (reader2->Read())
{
Object^ dHealthptr = reader2["effectOnHealth"];
Object^ dTirednessptr = reader2["effectOnTiredness"];
Object^ dHappinessptr = reader2["effectOnHappiness"];
...
There are two ways to prevent SQL Injection and the environment of SQLCLR does not change this:
The preferred mechanism is by using parameterized queries. Different languages and libraries go about this in different ways, but at the very least you should be able to use prepared statements. Please note that this does not apply to scenarios that could not accept a variable, such as with tableName and field in your code.
Please see:
Issuing a Parameterized Query
Using Stored Procedures
Sanitize the inputs:
Bare minimum, and by far the most common, requirement is to escape single quotes by doubling them (i.e. ' becomes '')
Additionally (below is a quote from a related answer of mine on DBA.StackExchange):
There is a lesser known type of attack in which the attacker tries to fill up the input field with apostrophes such that a string inside of the Stored Procedure that would be used to construct the Dynamic SQL but which is declared too small can't fit everything and pushes out the ending apostrophe and somehow ends up with the correct number of apostrophes so as to no longer be "escaped" within the string. This is called SQL Truncation and was talked about in an MSDN magazine article titled "New SQL Truncation Attacks And How To Avoid Them", by Bala Neerumalla, but the article is no longer online. The issue containing this article — the November, 2006 edition of MSDN Magazine — is only available as a Windows Help file (in .chm format). If you download it, it might not open due to default security settings. If this happens, then right-click on the MSDNMagazineNovember2006en-us.chm file and select "Properties". In one of those tabs there will be an option for "Trust this type of file" (or something like that) which needs to be checked / enabled. Click the "OK" button and then try opening the .chm file again.
So, be sure to properly size the string input parameters. You don't need VARCHAR(500) for a column that is declared as VARCHAR(25). Please see my answer on DBA.StackExchange for more details and examples:
Why does SQL Injection not happen on this query inside a stored procedure?
For tableName and field variables, those are being used as SQL identifiers in your query. You can't use either common method of query parameters or escaping. You just have to make sure to whitelist the values for those variables. In other words, check them against known identifiers of tables and columns in your database.
For the other variable fieldEntity, I suppose this should be used like a constant value in your SQL query. You can protect this from SQL injection by using a query parameter.
I don't know CLR, but there are lots of examples of using SQL query parameters in C++ or C#.
https://owasp.org/www-project-cheat-sheets/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html

Execute SQL Task -Full Result Set Datatype Mismatch Error

I am creating an SSIS package which has an execute SQL task and it passes result set variable to a for each loop container.
My Sql Query is:
Select distinct code from house where active=1 and campus='W'
I want the execute sql task to run this query and assign its results to a variable which is passed to a for each loop container which should loop through all the values in the result set.
But my execute sql task fails with error:
The type of the value (DBNull) being assigned to variable
"User::house" differs from the current variable type (String)
Now i have done my research and i have tried assigning the variable datatype Object but did not work. I tried using cast in my sql query and that also did not work.
Since my query returns multiple rows and one column, i am not sure how i can assign a datatype to the whole query?
Sample:
Code
AR
BN
CN
It sounds like you have a variety of issues in here.
Result Set
The first is in your Execute SQL Task and the need for agreement between the Result Set specification and the data type of the Variable(s) specified in the Result Set tab. If you specify Full Resultset, then the receiving object must be of type System::Object and you will only have 1 result set. The type of Connection Manager (ODBC/OLE/ADO) used will determine how you specify it but it's infinitely searchable on these fine forums.
The other two options are Single Row and XML. In 13 years of working with SSIS, I've never had cause to specify XML. That leaves us with Single Row. For a Single Row Result Set, you need to provide a variable for each column returned and it needs to be correctly typed.
To correct your issue, you need to declare a second variable. I usually call my rsObject (record set object) and then specify the data type as System.Object.
For Each Loop Container
Your For Each Loop Container will then be set with an Enumerator of "Foreach ADO Enumerator" and then the ADO object source variable will become "User::rsObject"
In the Variable Mappings, you'll specify your variable User::house to index 0.
Testing
Given a sample set of source source data, you can verify that you have your Execute SQL Task correctly assigning a result set to our object and the Foreach Loop Container is properly populating our variable.
SELECT DISTINCT
code
FROM
(
VALUES
('ABC', 1, 'w')
, ('BCD', 1, 'w')
, ('CDE', 0, 'w')
, ('DEF', 1, 'w')
, ('EFG', 1, 'x')
) house(code, active, campus)
WHERE
active = 1
AND campus = 'w';
If you change the value of campus from w to something that doesn't exist, like f then things will continue to work.
However, the error you're receiving can only be generated if the code is a NULL
Add one more entry to the VALUES collection like
, (NULL, 1, 'w')
and when the For Each Loop Container hits that value, you will encounter the error you indicate
The type of the value (DBNull) being assigned to variable "User::house" differs from the current variable type (String)
Now what?
SSIS variables cannot change their data type, unless they're of type Object (but that's not the solution here). The "problem" is that you cannot store a NULL value in an SSIS variable (unless it's of type object). Therefore you need to either exclude the rows that return a NULL (AND code IS NOT NULL) or you need to cast the NULL into sentinel/placeholder value as a substitute (SELECT DISTINCT ISNULL(code, '') AS code). If an empty string is a valid value, then you need to find something that isn't - "billinkcisthegreatestever10123432" is unlikely to exist in your set of codes but that might be a bit excessive.
Finally, think about renaming your SSIS variable from house to code. You might be able to keep things straight but some day you'll hand this code over to someone else for maintenance and you don't want to confuse them.
A picturesque answer https://stackoverflow.com/a/13976990/181965
the variable "User::house" is string , so , did you use it in result set?
you need declare son "object" var for result set
result set
then declare a string variable for every single Code from your result
For Each Loop Container
good luck

SSIS: How do I pass string parameter in an update statement in Execute SQL Task?

I have one execute SQL Task in SSIS 2012 which has update statement. I want to pass a string variable in the where clause of this update statement. The update section is as below:
where coalesce(s1.iteration, '') not like '%?%' and s2.Iteration = '?'
Here, ? needs to be replaced with a string variable, which in this case would be 08152017. I have added the variable to the Parameter Mapping. Screenshot is attached.
The task executes successfully but does not updates the value in the intended column. It seems the query is not passing the value.What am I doing wrong? How do I check that the SQL inside the Execute SQL Task is actually getting the value from the variable?
First of all, when you set your variable in parameter mapping, make sure the datatype is NVARCHAR and not LONG.
Second you need to write your statement like this:
where coalesce(s1.iteration, '') not like '%?%' and s2.Iteration = ?
You dont need '' because your variable is already set as a string.
Try to hardcode your value in your variable to see if it passes. Otherwise, set a breakpoint on pre-execute to see wheter your variable has a value.
If your variable has a value and your SQL is not working, maybe you should look into your SQL. Maybe try it directly in SSMS to see if it actually runs or does anything.
In scenario ? is Integer type variable, then please use below format:
SELECT ? +' Hello World!'
The above does not require the use of an additional string variable.
Create a user variable with % on it, for example, variable name is Like_Var with data type String
"%" + #Orig_Var + "%"
Let's say, Orig_Var has a value of 08152017, therefore Like_Var will have %08152017%
Then use Like_Var on your parameter in Execute SQL Task as parameter 0, data type VARCHAR in Parameter Mapping
WHERE COALESCE(s1.iteration, '') NOT LIKE ?
AND s2.Iteration = ?

Compare parameter in WHERE clause stored procedure

I have a stored procedures which receives a list of ItemId's. If the list only contain one item this works:
AND (#OrgItemIds = -1 OR ...)
AND (#OrgItemIds = -1 AND...)
but if the list contains more than one item it crashes.
Anybody knows how to fix this? Can I check the size of the list somehow?
Or can I check just the first element of the list like #OrgItemIds[0] or something like that?
There is no "comma-separated set of values" or "array" datatypes in SQL SERVER. You handle the parameter as a scalar variable in your code. Thus when you provide a single value in that list, server implicitly converts it to int and your sp succeeds.When you provide string with commas - it becomes impossible to convert it to int.You have to manually parse your argument, put it into a table variable and then use this table in WHERE clause. Or change handling of this argument to support a string of values instead of scalar value:
... where #OrgItemIds like '%,' + cast(t.OrgItemIds as varchar(10)) + ',%'
which is much worse for performance than filtering by id or list of ids.