Part of our solution is a page that displays company-specific information using an ASP Gridview. Our method of constructing the SQL that feeds the Gridview is by using C# to build a custom SELECT statement based on a series of user inputs.
Once the user applies their filters through a button click, C# loops through all of their selections (check boxes and text boxes) and then propagates those selections to a separate method which constructs a WHERE clause to append to a simple SELECT statement. We use a Table-Valued Function in the FROM statement, and the only input parameter is from the Querystring and this does not change throughout the process.
Once the query has been assembled using C#, we apply this query to the SqlDataSource as the Select Command. However, we have recently discovered a very bizarre SQL error that we haven’t seen before:
Errors :
"The variable name '#' has already been declared.
Variable names must be unique within a query batch or stored procedure."
We aren’t declaring any variables in our SQL. As stated above, the only input parameter comes from the Querystring, and we access this parameter using both QueryStringParameters in the ASP:SqlDataSource on the ASP side and “int.Parse(Request.QueryString["id"]).ToString()” on the C# side while constructing the SQL query.
After researching this error, I have yet to find an instance where the variable declaration is empty. Most people are getting errors similar to this when they have declared a variable such as '#email' or '#address' twice. We have no double declarations, and the fact that the variable in the error is not defined is causing a massive headache.
Has anyone seen anything like this before or have any suggestions on how to further debug?
I'll post some code if need be, but we are mostly interested to see if anyone has seen an error like this before.
Code:
string MainQueryStr = ResultsPages.SearchString(SearchVariables(), Request,
ProjectsSqlds, 0, "SELECT DISTINCT dbo.{0}.* FROM dbo.{0}(" + int.Parse(Request.QueryString["id"]).ToString() + ")",
"getXyzById", "AbcId");
StringBuilder SearchQueryStr = new StringBuilder();
SearchQueryStr.Append(MainQueryStr);
SearchQueryStr.Append(" ORDER BY AbcName");
ProjectsSqlds.SelectCommand = SearchQueryStr.ToString();
The search string function is a 500 line method that we can't post right now. It is used all over our solution and works as it should. It stitches together strings to create the query.
This is how the SearchString function appends the parameters:
l.Add(ResultsPages.NewSearchQueryString(ABCFiltersTxBx, SearchQueryStringVariableType.String,
"{1}.AbcID IN (" + ABCFiltersTxBx.Text + ")"));
Where the ABCFiltersTxBx is parsed into a comma separated string.
I should chime in as the supervisor in question here:
OK, so we figured out what was happening.
What we didn't realize was that the SQLDataSource was taking our appended WHERE clauses and using them as SelectParameters. Each parameter we wanted to add to the query that would ultimately feed the SQLDS was then being added as a SelectParameter without us realizing it, and because we hadn't made any explicit parameter declarations, the parameters were added with just "" as the name, leading to the error of "'#' has already been declared".
The most embarrassing part of this whole thing is that our API has already accounted for Parameter Names, but we had unwittingly excluded this part. Thank you all very much for reading and attempting to help. We thoroughly appreciate you taking your time to help us brainstorm our solution over here.
So I suppose the take-home of this whole error is in 2 parts:
Know your API. When you realize that you screwed it up on your own, graciously thank those that took the time to help you here on StackOverflow (or wherever you seek help), as their time is valuable as well.
"'#' is already declared" would indicate that you have parameters being declared without a name, so when debugging, look through the SQLDS you are using and find any parameters that haven't been explicitly named.
Again, thank you to all who read and offered to help. It's greatly appreciated.
Related
I am attempting to use ExecuteSqlInterpolated to update my database. However, it seems that there is a problem with my SQL parameters. Running the following code correctly updates intField1 and intField2, however stringField becomes the string "#p0".
_context.Database.ExecuteSqlInterpolated($"UPDATE table SET stringField='{someString}', intField1={someInt}, intField2={someOtherInt} WHERE id='{id}'");
I have already verified that my variables contain the desired values when the string is passed to the method. I understand that #p0 is what SQL uses to represent the first parameter in the query, but why isn't it being replaced by the string I gave it? I have also tried using ExecuteSqlRaw but ran into the same issue. My knowledge of SQL is limited at best, I know just enough to get by in web dev, so I'm guessing I'm committing some simple error in crafting the query here, but obviously I'm not sure.
I know it's late but just don't use quotation mark with your parameters, specially where you use int data-type
(delete single quotes)
_context.Database.ExecuteSqlInterpolated($"UPDATE table SET stringField={someString}, intField1={someInt}, intField2={someOtherInt} WHERE id={id}");
I have the following C# function
SomeFunction(string table, string column, string where) {
Sql sql = new Sql("SELECT ");
// [...] validate table and column values
sql.Append(column);
sql.Append(" FROM ");
sql.Append(table);
sql.Append(" WHERE ");
sql.Append(where); // This is the issue
}
As you can see this is awful, I'm dealing with this very old legacy code and changing the function signature and the way the clients use it is just not feasible. What I have to do is secure the 'where' clause. This clause may contain any number of conditions and data types.
I had a bunch of ideas but I don't think they are a good solution, I think this requires a properly written and tested code, but if I do it myself out of the blue it'll probably have holes. Here are some thoughts:
Splitting the string by char '=' -> what if that's not the condition operator
Find if string contains semicolons -> the SELECT clause remains vulnerable, and maybe one of the conditions contains that char so it'd give a false positive
If you have any idea/suggestion/pointing in the right direction I will be most grateful.
If the where clause is currently based on being a pre-composed string, then frankly I don't think it is a viable approach to attempt to "secure" it. It is theoretically possible, but any attempt at parsing the SQL will fail if the composed and compromised (injected) where clause is legitimate (but abusive). At that point: you've already lost track of the original intent. That's kinda the entire point of SQL injection: the resultant SQL is valid SQL - so it is very hard for you to tell the difference between where Name = 'Fred Orson' -- check name (probably fine) and where Name = 'Fred' Or 1=1 --' (injected - query widening).
So: while I acknowledge that you say:
changing the function signature and the way the clients use it is just not feasible.
Not changing the function signature doesn't really help you solve the problem. Trying to detect certain patterns is just an arms race, where you need to win every time and the attacker needs to win only once.
If it was me, I'd be doing something like:
[Obsolete("Please specify parameters separately - use 'null' if no parameters are needed")]
SomeFunction(string table, string column, string where) {
return SomeFunction(table, column, where, null);
}
SomeFunction(string table, string column, string where, object args) {
// ...
}
and using an approach like "Dapper" uses to compose the parameters from the args parameter - or just use "Dapper" itself to run the query, and use that functionality for free.
This approach:
prevents new uses of the dangerous API being added
lets the existing code continue to work for now
but lets you track how many outstanding problem calls there are, by watching the warnings
Edit: note: the point of the args parameter is to allow the caller to parameterize their inputs, i.e.
string name = ...
var users = SomeFunction("Users", "Id", "Name=#name", new { name });
With SomeFunction decomposing args and adding parameter name/value pairs from the properties on args (if it is non-null). There are various approaches to composing parameter sets, but the approach shown here is simple and easy to implement correctly - which makes it a clear win for me.
I got an error:
Must declare scalar variable '#20'
...when loading a webpage to fetch query data.
I've done a few web inquiries but i don't understand why a variable needs to be declared.
This started, when I had written out the code and loaded the page, it worked fine. Made a few modifications to unrelated parts of the page, refreshed again and started getting an error. I've never had to deal with declare before.
var db = Database.Open("StarterSite");
var selectQueryString = "SELECT * FROM business_Customer WHERE memberid=#20";
var memberid = Database.Open("StarterSite").QueryValue("SELECT memberid FROM business_Customer WHERE email=#0",WebSecurity.CurrentUserName);
That's the script at the top of my page, right before the html loads.
foreach (var row in db.Query(selectQueryString, memberid)) {...}
...is the script that is giving me the error.
But before modifying and adding to the page, the memberid variable worked just fine. The value is an integer, and i used it separately, by itself to confirm that it works. I tried changing the snippet to foreach (var row in db.Query(selectQueryString, (int) memberid)) {...} and it gave me an error that said:
Cannot convert null to 'int' because it is a non-nullable value type
I'm extremely confused... What does declaring have to do with calling a value from an sql database? Is there a simple solution to my problem? i read other stackoverflow posts related to this, but the solutions were written in SQL syntax, and used "Declare", there wasnt enough resources for me to understand how to apply their solution to my problem.
I discovered what was wrong with my script. It was missing an external javascript file, needed for another set of values, that somehow had something to do with error. But... my original for my original question, is declaring a variable something i need to know? I dont mind studying it (if there were resources that went into details on what it's for, or when it's used), but i've never had to deal with it before... is it something "important/valuable"?
You should be using #0 - this represents the first argument.
Query and QueryValue both take 2 parameters - a commandText and an array of parameters. To specify where the parameters go in your commandtext, you use #0, #1, etc.
For example:
var selectQueryString = "SELECT * FROM business_Customer WHERE memberid=#0";
db.Query(selectQueryString, memberid)
So when you call Query, it will replace #0 with the value of the memberid variable.
I'm working a C# form application that ties into an access database. Part of this database is outside of my control, specifically a part that contains strings with ", ), and other such characters. Needless to say, this is mucking up some queries as I need to use that column to select other pieces of data. This is just a desktop form application and the issue lies in an exporter function, so there's no concern over SQL injection or other such things. How do I tell this thing to ignore quotes and such in a query when I'm using a variable that may contain them and match that to what is stored in the Access database?
Well, an example would be that I've extracted several columns from a single row. One of them might be something like:
large (3-1/16" dia)
You get the idea. The quotes are breaking the query. I'm currently using OleDb to dig into the database and didn't have an issue until now. I'd rather not gut what I've currently done if it can be helped, at least not until I'm ready for a proper refactor.
This is actually not as big problem as you may see it: just do NOT handle SQL queries by building them as plain strings. Use SqlCommand class and use query parameters. This way, the SQL engine will escape everything properly for you, because it will know what is the code to be read directly, and what is the parameter's value to be escaped.
You are trying to protect against a SQL Inject attack; see https://www.owasp.org/index.php/SQL_Injection.
The easiest way to prevent these attacks is to use query parameters; http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlparameter.aspx
var cmd = new SqlCommand("select * from someTable where id = #id");
cmd.Parameters.Add("#id", SqlDbType.Int).Value = theID;
At least for single quotes, adding another quote seems to work: '' becomes '.
Even though injection shouldn't be an issue, I would still look into using parameters. They are the simpler option at the end of the day as they avoid a number of unforeseen problems, injection being only one of them.
So as I read your question, you are building up a query as a string in C#, concatenating already queried column values, and the resulting string is either ceasing to be a string in C#, or it won't match stuff in the access db.
If the problem is in C#, I guess you'll need some sort of escaping function like
stringvar += escaped(columnvalue)
...
private static void escaped(string cv) as string {
//code to put \ in front of problem characters in cv
}
If the problem is in access, then
' escapes '
" escapes "
& you can put a column value containing " inside of '...' and it should work.
However my real thought is that, the SQL you're trying to run might be better restructured to use subqueries to get the matched value(s) and then you're simply comparing column name with column name.
If you post some more information re exactly what the query you're producing is, and some hint of the table structures, I'll try and help further - or someone else is bound to be able to give you something constructive (though you may need to adjust it per Jet SQL syntax)
I'm trying to use a RefCursor as an input parameter on an Oracle stored procedure. The idea is to select a group of records, feed them into the stored procedure and then the SP loops over the input RefCursor, doing some operations to its records. No, I can't select the records inside the SP and thus avoid having to use the RefCursor as an input type.
I've found an example on how to do this on (this here would be the link, but it seems I cannot use them yet) Oracle's documentation, but it uses a simple SELECT to populate the input RefCursor; and therein lies the rub: I've got to populate it from code.
You see, in code I have this:
[OracleDataParameter("P_INPUT", OracleDbType.RefCursor, ParameterDirection.Input)]
private List<MiObject> cursor;
And, I've tried populating cursor with a List<T>, a DataTable, even an plain array of MyObject, and nothing works. When I try running my tests I get an error:
"Invalid Parameter Linking"
Maybe not the exact wording, as I'm translating from Spanish, but that's the message
Any ideas?
I'm also in contact with Mark Williams, the author of the article I've tried to link on my post, and he has kinly responded like this:
"
It is no problem to send me email; however, I think I will disappoint you with my answer on this one.
Unfortunately you can't do what you are trying to do (create a refcursor from the client like that).
A couple of problems with that are that a refcursor refers to memory owned by Oracle on the server and Oracle has no concept of client items like a DataTable or a .NET List, etc.
Do you have any other options available other than using a refcursor?
"
So basically I'm screwed, and this question is closed. Thanks for reading and/or trying to help, you all.
From memory, isn't there an OracleCursor class somewhere in the ODP.NET library that works?
Look at this sample for refcursor as input to pl/sql from oracle technet.
The clou is that the input refcursor object must be created by oracle themself. You cannot convert a list or anything else to refcursor.