Execute query only if it is select statement. How? - c#

string sqlQuery = "unknown";
I need to write a function which receives a sql query as parameter e.g. sqlQuery. I would like to execute it only if it is select statement and return data. In other case, if parameter sqlQuery contains delete, update or truncate, the function should return null.
I wonder if there is way to achieve this without parsing contents of parameter sqlQuery.
I would like to do this using c sharp for oracle queries.
Any tips. Thanks.
Update:
This should work for all kinds of users with all privileges.

Run the query in the context of a user who only has select privileges. Any other type of query will error out.

SET TRANSACTION READ ONLY, then execute the string. If it attempts to modify data, it will generate an ORA-01456 error. You can trap this and return whatever you want.

If you really have to work with a constructed string that will operate on the database, you should use the DBMS_ASSERT database package to make sure you have a pure query that's not subject to SQL injection. There's a nice paper on the Oracle site about that here.
The basics are:
only give the minimum privileges necessary, for example only giving the user "select" as described in an earlier reply. And then only on the minimum necessary set of tables. Views are really helpful here in limiting access.
Use bind variables where that's possible.
If you can't use bind variables then check the purity of your statement using DBMS_ASSERT

You can probably search the string for keywords like "update", "delete", "truncate" and all the other ways you can do ddl or dml on the table, but it is very error-prone. You have to eliminate strings in the query which might have these keywords and there are a lot of keywords that you have to take into account.
If your requirement is to return null, Why not give just the select privilege on the necessary objects and return null if you encounter the Insufficient Privileges error?
http://download.oracle.com/docs/cd/E11882_01/server.112/e17069/strms_trapply.htm#STRMS1065

I would not allow the client to specify a SQL select string. Too many possible attack vectors.
Have you considered using Linq? The caller could pass a Func<T, bool> that could be passed to a Where clause. Since Linq will generate the select statement for you, there's no possibility of a non-select statement occuring.

Bear in mind a SELECT column FROM table FOR UPDATE will still take an exclusive lock on every row on that table. And it only needs SELECT privileges (none of INSERT, UPDATE or DELETE are required).

You can use ADO.NET SqlCommand http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.aspx. It has methods ExecuteReader for a select type query and ExecuteNonQuery for other sql expression, you jest set the CommandText string attribute. If I'm right it throws exception if the query is not a select in ExecuteReader but you must check it.

string sqlQuery = "("+evil_sql+")";
Only a subquery can start with a parentheses. This will stop DML, DDL, and the FOR UPDATE issue that Gary mentioned. You still have to execute everything, just catch all the errors. I've done this on a public-facing website without any issues.
Even if your user is not directly granted anything you'll need to check for unnecessary PUBLIC grants. And of course keep your system patched. There have been exploits in functions that can be called in a SELECT.

Related

What to use in this SqlDataAdapter?

I have a SqlDataAdapter that looks something like:
("Select prodID, CatalogType, prodName, catID, Integration, ItemProfileCatalogID From ShoppingCart t1
INNER JOIN ItemCatalogProfile t2 ON t1.CatalogType = t2.CatalogType
WHERE SessionID = '" + Session["ID"] + "' AND CatalogID ='" + Session["Customer"] ....)
there are a few more included in the where statement, but the one I cannot seem to get to work is:
ItemProfileCatalogID ..
I need to include this to narrow down the items down to only one of each and this variable will do just that if I can figure out what to use in this statement.
I've tried
viewstate[""]
Request.QuerryString[""]
Session[""]
and I cant seem to get those to work..
The problem I am having is, the current shopping cart if I do not have this item to filter, it will return every instance of that particular product in the database because there are up to 250 listings of one item for different catalogs, and that is where the ItemProfileCatalogID comes in, that will filter it down to just the one item
Any suggestions?
Thank you
CatalogID is probably numeric and you are using it as a string in your SQL statement.
It could also be that you have some syntax error in your SQL statement not easily detectable with all the string concatenation going on or due to CatalogID containing a naughty character (' for example)
Also, please check out parametrized queries; concatenation when building SQL statements is usually not a very good idea (SQL injection).
As InBetween said it probably is an issue with it being numeric, you should be able to use the .toString() method to solve this.
Also Stored procedures are generaly more advisable to use for a number of reasons, in your case you mentioned you are working with a shopping cart which suggests maybe some kind of ecommerce application? Using a stored procedure here will mean that SQL does not have to compile your select statement every time you run it thus improving performance and having the code in a stored proc will also increase maintainability and possibly allow you to reuse the procedure in other places.
Added to this you are not actually transmitting the select statement over the network which increases security as no one can intercept this and gain insight into your table structures, and as InBetween mentioned above a stored procedure will help you protect against injection attacks or just simply unforseen data as you can add error handling more easily.

Is order of parameters for database Command object really important?

I was debugging a database operation code and I found that proper UPDATE was never happening though the code never failed as such. This is the code:
condb.Open();
OleDbCommand dbcom = new OleDbCommand("UPDATE Word SET word=?,sentence=?,mp3=? WHERE id=? AND exercise_id=?", condb);
dbcom.Parameters.AddWithValue("id", wd.ID);
dbcom.Parameters.AddWithValue("exercise_id", wd.ExID);
dbcom.Parameters.AddWithValue("word", wd.Name);
dbcom.Parameters.AddWithValue("sentence", wd.Sentence);
dbcom.Parameters.AddWithValue("mp3", wd.Mp3);
But after some tweaking this worked:
condb.Open();
OleDbCommand dbcom = new OleDbCommand("UPDATE Word SET word=?,sentence=?,mp3=? WHERE id=? AND exercise_id=?", condb);
dbcom.Parameters.AddWithValue("word", wd.Name);
dbcom.Parameters.AddWithValue("sentence", wd.Sentence);
dbcom.Parameters.AddWithValue("mp3", wd.Mp3);
dbcom.Parameters.AddWithValue("id", wd.ID);
dbcom.Parameters.AddWithValue("exercise_id", wd.ExID);
Why is it so important that the parameters in WHERE clause has to be given the last in case of OleDb connection? Having worked with MySQL previously, I could (and usually do) write parameters of WHERE clause first because that's more logical to me.
Is parameter order important when querying database in general? Some performance concern or something?
Is there a specific order to be maintained in case of other databases like DB2, Sqlite etc?
Update: I got rid of ? and included proper names with and without #. The order is really important. In both cases only when WHERE clause parameters was mentioned last, actual update happened. To make matter worse, in complex queries, its hard to know ourselves which order is Access expecting, and in all situations where order is changed, the query doesnt do its intended duty with no warning/error!!
Within Access, an ADODB.Command object ignores parameter names. In fact I can refer to a parameter using a bogus name (which doesn't even exist in the SQL statement) and ADO doesn't care. All it seems to care about is that you supply parameter values in the exact same order as those parameters appear in the SQL statement. BTW, that is also what happens if I build the SQL statement with ? place-holders instead of named parameters.
While I realize that your question is about c# and OleDbCommand, it looks to me like Dot.Net's OleDbCommand may be operating the same as Access' ADODB.Command. Unfortunately, I don't know Dot.Net ... but that is my hunch. :-)
The order is important because of the use of ? placeholders in the command string.
If you want to list the parameters in any order, it's best to use named parameters, such as #word, #sentence, etc.
condb.Open();
OleDbCommand dbcom = new OleDbCommand("UPDATE Word SET word=#word,sentence=#sentence,mp3=#mp3 WHERE id=#id AND exercise_id=#exercise_id", condb);
dbcom.Parameters.AddWithValue("#id", wd.ID);
dbcom.Parameters.AddWithValue("#exercise_id", wd.ExID);
dbcom.Parameters.AddWithValue("#word", wd.Name);
dbcom.Parameters.AddWithValue("#sentence", wd.Sentence);
dbcom.Parameters.AddWithValue("#mp3", wd.Mp3);
I have been doing some tests with using OleDbCommand and its parameters collection against an Access DB. The ordering of parameters is of course necessary, since this is a limitation of the OLE DB .NET provider. But there is a problem that you can encounter when using question marks as place holders.
Say you have a query ("stored procedure") in your Access DB that looks like this, very simplified here:
parameters
prmFirstNumber Long,
prmSecondNumber Long;
select
fullName
from
tblPersons
where
numberOfCars < prmFirstNumber And
numberOfPets < prmSecondNumber And
numberOfBooks beteween prmFirstNumber And prmSecondNumber
Here you see that simply changing to question marks would break the query.
I have found though, as a solution to this, that you can actually use names for parameters. So you can let the query above remain as it is. You just have to use the same order when you run the query. Like in this case, you first add the parameter prmFirstNumber and then prmSecondNumber, and then you run the query.
When reusing parameters, i.e. executing a query more than once and setting new values for the parameters each time, one must call the prepare method of the command object right after having defined the parameters. There are some details there that need to be fulfilled too, look at the documentation for "prepare". Not calling prepare causes strange behaviour without error messages which can corrupt your database or cause wrong information to be presented to users.
I can add also that when queries are stored in the Access DB with parameters specified, like in my example above, then the ordering of the parameters is unambiguously defined by the parameters-section.
I also made a routine, "retrieveDeclaredJetParametersInOrder", which automatically populates an OleDbCommand object with those named parameters, in the correct order. So my code can look like this:
Dim cmd As New OleDbCommand("qryInAccessDB", Conn)
cmd.CommandType = CommandType.StoredProcedure
Conn.Open()
retrieveDeclaredJetParametersInOrder(cmd)
cmd.Parameters("prmOneOfTheParametersPerhapsTheLastOneDeclared").Value = 1
cmd.Parameters("prmAnotherone").Value = 20
cmd.Parameters("prmYetAnotherPerhapsTheFirstOneDeclared").Value = 300
cmd.ExecuteNonQuery()
Conn.Close()
So, as you see, I can handle it as if parameters are named, and never have to bother with their ordering.
The retrieveDeclaredJetParametersInOrder of course adds extra time to execution, since it involves an extra call to the DB, where it retrieves the SQL-text and then parses out the parameter names and types.

SQL column true or false - use a view?

Simple question, I have an application and dependant on whether you are in the administration system or on the public website I want to show different results.
Example: in the database for a news story in the administration I may set the column value 'showonsite' to false. So I would like this to show in the administration panel only and not on the live site.
My question is, because I require the same information just with the only one column change, live site to only show true values and administration to show both. What is the most effective way of achieving this without copy paste of code?
Thanks
Please see the second query.
I may be missing something. For non-adminstrative users, could you simply not SELECT news stories with the showonsite column set to false?
For example:
SELECT
*
FROM
dbo.NewsStory
WHERE
showOnSite = 1
Edit:
Oh! I see. You're talking about copying and pasting the query. It's late. >.<
If you have a stored procedure, you can pass a value to indicate whether the given user is an administrator:
SELECT
*
FROM
dbo.NewsStory
WHERE
showOnSite = 1
OR #isAdmin = 1
Depends on where you write your code - if you use stored procedures, then just a parameter like #liveonly should be sufficient. Sameway if you use an inline sql query directly in a C# method, then a parameter live_only should be good enough, to determine whether the extra where condition will be added or not.
For eg. in the SP
SELECT *
FROM news
WHERE ((#live_only=true AND showonsite=true) or (#live_only=false))
This way it can be called with #live_only = false for admin panel, but with value true for the actual site.
No, I wouldn't use a view for that, an I wouldn't copy any code. It sounds like this is just a conditional filter on your queries.
You don't make it clear how you are doing the data-access, but this might mean aasdij an extra line of TSQL, adding adding a so parameter, adding an extra LINQ .Where clause, etc

SQL Table Parameters

Why table params aren't allowed in SQL Server? Is there any solution to this?
Example:
using (SqlCommand myCommand = new SqlCommand("SELECT * FROM #table WHERE USERNAME=#username AND PASSWORD=HASHBYTES('SHA1',
#password)", myConnection))
{
myCommand.Parameters.AddWithValue("#table", table);
myCommand.Parameters.AddWithValue("#username", user);
myCommand.Parameters.AddWithValue("#password", pass);
myConnection.Open();
SqlDataReader myReader = myCommand.ExecuteReader())
...................
}
Thanks.
You can't paramaterise that part of the SQL. The server needs to know the name of the table to be able to 'prepare' the query, which is done before the parameters are processed.
You might dynamically generate the query, but that may open you up to SQL injection attacks and run-time SQL syntax errors. Also, there is a saving to be had if an SQL statement can be cached by the server - you'll loose that if every query is dynamically generated.
Why? Because the benefit of flexibility is minor compared to the nightmare it would create in query optimization and validation.
As a sidenote, even if it was recognised you'd be getting a quoted string in the SQL, not just the table name. Dynamic SQL with heavy validation is the only real way of doing this.
If you have to pass a table of values...
XML parameter
CSV (String) parameter
Parse in SQL. See "Arrays and Lists in SQL Server 2005"
Otherwise, what are you trying to do?
Edit: I've got it now. As others mentioned, SQL does not work like that.
No, you cannot pass the table name as a param.
The best way would be to try using String.Format for the table name.
I would try to ilustrate my point of view about this with an example:
If you go to buy a car, you can "parametrize" some thinks: You can change the colour, may be some variations of the engine, you can put an MP3 or not, ... but you cant change the car model. If you change the car model, this is not a parameter, this is another car.
It is the same with sql query, the table is not a parameter is part of the sentence itself, same way that the command is (select, update) .. so you can't do #command from #table. If you change the table, this is another sentence, like the car.
(this is not a technical "because" answer for you question, but a conceptual point of view for better understanding of the techical part that others are posting)
My two cents.

Regex to Extract Update Columns from a Sql Statement

I need a Regex Statement (run in c#) that will take a string containing a Sql Update statement as input, and will return a list of columns to be updated. It should be able to handle columns surrounded by brackets or not.
// Example Sql Statement
Update Employees
Set FirstName = 'Jim', [LastName] = 'Smith', CodeNum = codes.Num
From Employees as em
Join CodeNumbers as codes on codes.EmployeeID = em.EmployeeID
In the end I would want to return an IEnumerable or List containing:
FirstName
LastName
CodeNum
Anyone have any good suggestions on implementation?
Update: The sql is user-generated, so I have to parse the Sql as it is given. The purpose of extracting the column names in my case is to validate that the user has permission to update the columns included in the query.
You're doing it backwards. Store the data in a broken out form, with the table to be updated, the column names, and the expressions to generate the new values all separate. From this canonical representation, generate both the SQL (when you need it) and the list of columns being updated (when you need that instead).
If you absolutely must pull the column names out of a SQL statement, I don't think that regular expressions are the correct way to go. For example, in the general case you may need to skip over new value expressions that contain arbitrarily nested parenthesis. You will probably want a full SQL parser. The book Lex & Yacc by Levine, Mason, and Brown has a chapter on parsing SQL.
Response to update:
You are in for a world of hurt. The only way to do what you want is to fully parse the SQL, because you also need to make sure that you don't have any subexpressions that perform unauthorized actions.
I very, very strongly recommend that you come up with another way to do whatever it is that you are doing. Maybe break out the modifiable fields into a separate table and use access controls? Maybe come up with another interface for them to use in specifying what they want done? Whatever it is that you're doing, there is almost certainly a better way to do it. Down that path there be dragons.
Regular expressions cannot do this task, because SQL is not a regular language.
You can do this, but not with a regular expression. You need a full-blown parser.
You can use ANTLR to generate parsers in C#, and there are free grammars available for parsing SQL in ANTLR.
However, I agree with Glomek that allowing user-supplied SQL to be run against your system, even after you have tried to validate that it includes no "unauthorized actions," is foolish. There are too many cases that may circumvent your validation.
Instead, if you have only a single text field, you should define a simplified Domain-Specific Language that permits users to specify only actions that they are authorized to do. From this input, you can build the SQL yourself.
SQL has a complex recursive grammer, and, there will always be some sub select, group by, or literal that will break your regex based parser.
Why don't use a sql parser to achieve what you need, here is an article shows you how to achieve what you need within 3 minutes.

Categories