An intern has written the following vb.net code:
Public Sub PopulateUsersByName (name As String)
'Here, the parameter 'name' is unsantized, coming straight from the form
Dim query As String = ""
query += vbNewLine + " SELECT Id, FirstName, LastName FROM dbo.[Users]"
query += vbNewLine + " WHERE FirstName LIKE '" + REPLACE(name, "'", "''") + "'"
query += vbNewLine + " OR LastName LIKE '" + REPLACE(name, "'", "''") + "'"
'Execute SQLCommand with above query and no parameters
'Then do some other stuff
END Sub
I have explained that in general, one should never use string concatenation when trying to do something like the above. The best practice is to use either an SP, or an SQLClient.SQLCommand with parameters.
His logic is: any sql varchar(xxx) gets sanitized by replacing all single quotes with two single quotes (and adding additional single quotes at the start and end of the string).
I am unable to provide an example of something the user could type that would get around this - I'm hoping I can find something more convincing than "But, as a general principal, one should avoid this - you know... coz... well, don't argue with me - I'M THE BOSS AND MY WISH IS YOUR COMMAND.".
Note: The code will always connect to our Microsoft SQL Server. But I can imagine it failing to sanitize the input on some other SQL Implementation.
UPDATE:
Just to make it a little clearer, what I'm looking for is a possible value of the parameter name which will allow someone to inject SQL into the query.
Try this with his code using '%_%' (with and without the single quotes) as the input....same as this in SQL....
select SELECT Id, FirstName, LastName FROM dbo.[Users] from TBL_EMPLOYEES where FirstName like '%_%' or LastName like '%_%'
irrespective if it failing or not, his is very poor code... fair enough this one is only a one liner, but anything more than that, including complicated SQL statements would be difficult to maintain and debug.. in any case, using Sps gets him used to using them AND allows him to take advantage of the flexibility and power of T-SQL... LOL, i'd dread to think how some of the SQL I have written would look like in code.....
You know, I come across code like this (and a lot worse) all the time... just because it might work for a while DOESN'T mean it's the right way to do it.... If your intern that does NOT listen to experience he will NEVER make a good developer and that is sadly a fact
I once reduced a junior developers attempt at importing a CSV file (50 million rows) in the same way your intern has done, from her 300 lines of code (which was never going to work) to just one line of LINQ to convert it to XML (we couldn't use Bulk Insert or bcp) and a fancy SP... Was bullet proof, job done....
I can get a list of all your users. If name = %%
Now I know the full name of everyone in your database.
I would consider that a security hole.
Sanitizing is not the answer. You can by pass quotes and use "smuggling". A good example is http://danuxx.blogspot.com.br/2011/08/sql-injection-bypassing-single-quotes.html
Also a good practice (to use dynamic queries) is to use parametric dynamic queries. Also SPs can do the trick (if you don't use dynamic queries inside it off course).
Related
I have to make a SQL string to be injected in a database for a third party to read it, execute it, and making a report with the results. Since the user can choose the columns wanted for the report, as well as renaming the columns, I've ended with a code like this:
string sql = "SELECT ";
foreach(KeyValuePair<string, string> field in report.fields)
{
sql += "[" + field.Key + "] as [" + field.Value + "];
}
sql += " WHERE idrpt=#id";
The only part of this query I can parametrize is the WHERE clause, but if my research on the web has'nt been misguided, there's no way to parametrize the column names and aliases in the SELECT clause. Now, given that I can't change the way the program works (I have to produce a valid SQL query to a third party to execute it), what would be the best way of sanitize the input string?
I've solved the part about column names by checking them against a list of valid columns, but I can't do that for the aliases, which can be whatever string of less than 80 characters the user is willing to give.
Right, so you have a SQL layout you can't change that necessitates you to do this. That is unfortunate, but lets make the best of it.
As you stated in your comments, you probably need some special character support, so specifically escape these special characters.
Other then that, you should reduce the allowed names to alphanumeric characters and possibly whitespace. Validate these against your validation mechanism of choice, for instance regex, and allow only those characters. That will probably keep you mostly safe from SQL injection.
This isn't optimal, but it seems to be the best you can do in this situation.
As you noted there is no way to parametrize the column names and aliases. Therefore, you are opened to SQL injection. To minimize the issue you can use quotename, which is similar to the approach you are using currently.
string sql = "SELECT ";
foreach(KeyValuePair<string, string> field in report.fields)
{
sql += "quotename(" + field.Key + ") as quotename(" + field.Value + ")";
}
sql += " WHERE idrpt=#id";
I am exposing a web service that constructs a SQL SELECT statement and accepts parameters from a controller. The generic functions looks like this:
DataTable SqlSelect (string select, string from, string Where, string orderby = "", string groupBy = "")
{
string sql = "SELECT " + select + " FROM " + from + " WHERE " + where
(orderby =="") ? "" : "ORDER BY " + orderby ...
//do other stuff
}
Now what makes me worry is the fact that base on given function above, the user may now inject harmful commands like:
SqlSelect("DROP TABLE 'TABLENAME'", "INFORMATION_SCHEMA.TABLES", "TABLE_NAME like %%'");
Which I want to prevent.
Now my question is: what is the best thing I can do to prevent user to UPDATE, MODIFY, DELETE, TRUNCATE tables and only allow SELECT statement (can use something like READ oNLY?)
Note: this is similar to this question but the user was working on PHP, while I'm in ASP.NET MVC, also what I want to achieve here is only allow SELECT or 'GET' statement.
Do not do it this way. You need to parameterize your queries which means that you cannot accept SQL text as an input. I have found many attempts by developers to detect SQL injection attacks and I almost always find a way of getting past their logic.
If you need to be able to dynamically construct any SELECT query based on any table in your database then you could easily create a class that indicates the table, select columns and where predicate columns as enums, and the values of the where predicates. Concatenate the SQL text based on this class and include the predicate values using SqlParameters.
It is just one example, but for sure you do not want to accept SQL text.
Make use of a data reader https://msdn.microsoft.com/en-us/library/haa3afyz(v=vs.110).aspx. You can catch any exceptions when you call ExecuteReader().
However I would advise against exposing this kind of generic functionality to client side code. You should rather only provide controlled access to your data via an appropriate data layer using something like the repository pattern.
Let me start by saying I am biased; I hate dynamic SQL under all circumstances. That being said, is this scenario considered good practice for dynamic SQL?
sqlDataSourceObject.SelectCommand = String.Concat(
"select top ", maxRows,
" col1, ",
" col2 as myData, ",
" '' as blah, ",
" col3 as Fromperson ",
" 'Corporate' as toPerson, ",
" Convert(char(11), orderDate) as orderDate, ",
" carrier, ",
sqlString1,
sqlString2,
sqlString3 + " AND areaCode = '" + currArea + "'"
);
This query may run once, then change the value for sqlString1,2,3, or currArea and run it again against a different SqlDataSource.
This code makes me angry to read. Its hard to read, it can change with the sqlString variables, I cant run it without copy/pasting into SSMS and I have to go track down several variables to make a single change.
But, like I said I am biased so I am asking you. Is this code, written in 2001 before LINQ, as good as a stored proc or some other technology, generally OK from a good practice perspective?
If not, how would you have improved it (remember no LINQ, this is 2001).
A point of clarification:
Dynamic SQL is normally taken to mean that the semantics of the statement change based on some external factor. In other words, the column names or even the base table(s) might be altered. This was common to do for pivot queries in the old days.
It's kind of hard to tell because I don't know what's going into those awfully-named sqlStringX parameters, but I think that what I'm seeing here is really just inline SQL which happens to be riddled with SQL injection vulnerabilities. It is trivially easy to parameterize. Fix this ASAP, please. Inline SQL is fine but there is no reason to be using raw strings instead of parameters.
Stored procedures would be one idea of how to better handle these types of queries. Granted the stored proc may just execute what the parameters pass but that would be my suggestion for one way to improve that code so that the DBA can know what indexes may be useful to help optimize the query. SQL injection attacks as #Jarrod Roberson points out are also quite likely with this kind of code.
PS: I wrote this kind of code back in 1998 where I had ~20 possible parameters in writing a "Find Customer" routine that was one of my first assignments out of university so I do understand where this kind of code can originate.
I'd use a stored procedure myself. But in any case, no matter what, use parameters. They way you' have it there is not secure at all, and as you say, makes me angry to look at. :-)
Here's one reference that might help (not stored procs per se, but still uses parms)
http://www.asp.net/data-access/tutorials/using-parameterized-queries-with-the-sqldatasource-vb
I built a prototype system with some database queries. Since it was just a prototype and I was very new to databases, I just used a direct string. This is the string I used and it works fine:
command = New OleDbCommand("SELECT * FROM " + prefix + "CanonicForms WHERE Type=1 AND Canonic_Form='" + item + "'", dictionary_connection)
Now, in putting it in a real system, I wanted to use the more secure parametized method, so after some googling I came up with this:
command = New OleDbCommand("SELECT * FROM #prefix WHERE Type=1 AND Canonic_Form=#form", dictionary_connection)
command.Parameters.AddWithValue("#prefix", prefix + "CanonicForms")
command.Parameters.AddWithValue("#form", item)
But all I get is an error for an incomplete query clause. What have I done differently between the two?
Your table name can't be a parameter. You might have to do some form of concatenation. It's not really a parameter in the formal sense of the word.
As ek_ny states, your table name can not be a parameter.
If you are really paranoid about injection then check the passed in table name against a white list of allowable values prior to buidling up the SQL string.
#cost, I agree that it would be nice but its just not a feature that exists. Generally its assumed that your WHERE clauses are the dynamic portion of the query and everything else, including the SELECT list and the table name, are static. In fact, most of the WHERE clause isn't even dynamic, only the search values themselves. You can't dynamically add column names this way either. This could definitely be built but it would require that the query engine be more aware of the database engine which is a can of worms that Microsoft didn't feel like opening.
Imagine a drop down menu with table names such as 'Jobs', 'People', 'Employers' that some naive developer built. On postback that value is used to SELECT * FROM. A simple SQL injection would be all that it takes to jump to another table not listed in that menu. And passing something really weird could very easily throw an error that could reveal something about the database. Value-only parametrized queries can still be broken but the surface area is much smaller.
Some people with multiple table prefixes use schemas, is that an option for you?
Am storing a table name in a String
ugad = "INSERT INTO tb(Ugname,Ugdob,Uggender)"
this is the ordinary query which functions well.
But i need to store a Tablename in a string called "dept"
and this string will have diff table name at diff times. How should i run it, Wat query should I Give.
ugad = "INSERT INTO dept(Ugname,Ugdob,Uggender)"
I know this query is not vaild. May i know the correct query
Use:
ugad = "INSERT INTO " & dept & "(Ugname,Ugdob,Uggender)"
N.B. There are arguably safer, better ways to compose SQL (if you are worried about malicious or accidental interference with your underlying data through SQL injection) than the above but hopefully that gets you started.
Or
ugad = String.Format("INSERT INTO {0}(Ugname,Ugdob,Uggender)", dept)
Which I think is easier to read and easier to maintain.
If I understand you correctly you neet to try something like
ugad = "INSERT INTO " + dept + "(Ugname,Ugdob,Uggender)"
Have a llok at Operators in VB.NET
Just remember that string concatenation can be very slow once you start concatenating in loops, so always have in the back of your mind that the StringBuilder Class exists, and is a lot faster than normal concatenation...