Best way of sanitize unparametrizable sql - sql

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";

Related

Filtering Harmful SQL Select Statements in ASP.NET

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.

SQL Injection: Why is the following code dangerous?

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).

vb.net handling many table name using string

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...

What is the smallest possible SQL injection attack character sequence?

Simple, a SQL injection attack in as few characters as possible. Note, I'm not trying to prevent SQL injection attacks by limiting inputs to a certain size, but rather am genuinely curious how many characters is needed to execute even the simplest attack.
For posterity sake, let's say the smallest table name is 4 characters, e.g., "user". Please factor that in.
1 Character is the smallest unit that you have control over. The question depends heavily on what you're doing. For instance, if you're dealing with an interface to delete your profile from a site, and you send '%' instead of your name:
"Delete from Users where name like '"+username+"'"
then setting your username to % will delete all the users.
When injecting into a string literal:
';drop database;--
Suppose the query was generated like this
"Select * from user where userid = " + myVar
1; delete from user;
how about this one:
' OR 1=1 --

Is there some way to inject SQL even if the ' character is deleted?

If I remove all the ' characters from a SQL query, is there some other way to do a SQL injection attack on the database?
How can it be done? Can anyone give me examples?
Yes, there is. An excerpt from Wikipedia
"SELECT * FROM data WHERE id = " + a_variable + ";"
It is clear from this statement that the author intended a_variable to be a number correlating to the "id" field. However, if it is in fact a string then the end user may manipulate the statement as they choose, thereby bypassing the need for escape characters. For example, setting a_variable to
1;DROP TABLE users
will drop (delete) the "users" table from the database, since the SQL would be rendered as follows:
SELECT * FROM DATA WHERE id=1;DROP TABLE users;
SQL injection is not a simple attack to fight. I would do very careful research if I were you.
Yes, depending on the statement you are using. You are better off protecting yourself either by using Stored Procedures, or at least parameterised queries.
See Wikipedia for prevention samples.
I suggest you pass the variables as parameters, and not build your own SQL. Otherwise there will allways be a way to do a SQL injection, in manners that we currently are unaware off.
The code you create is then something like:
' Not Tested
var sql = "SELECT * FROM data WHERE id = #id";
var cmd = new SqlCommand(sql, myConnection);
cmd.Parameters.AddWithValue("#id", request.getParameter("id"));
If you have a name like mine with an ' in it. It is very annoying that all '-characters are removed or marked as invalid.
You also might want to look at this Stackoverflow question about SQL Injections.
Yes, it is definitely possible.
If you have a form where you expect an integer to make your next SELECT statement, then you can enter anything similar:
SELECT * FROM thingy WHERE attributeID=
5 (good answer, no problem)
5; DROP table users; (bad, bad, bad...)
The following website details further classical SQL injection technics: SQL Injection cheat sheet.
Using parametrized queries or stored procedures is not any better. These are just pre-made queries using the passed parameters, which can be source of injection just as well. It is also described on this page: Attacking Stored Procedures in SQL.
Now, if you supress the simple quote, you prevent only a given set of attack. But not all of them.
As always, do not trust data coming from the outside. Filter them at these 3 levels:
Interface level for obvious stuff (a drop down select list is better than a free text field)
Logical level for checks related to data nature (int, string, length), permissions (can this type of data be used by this user at this page)...
Database access level (escape simple quote...).
Have fun and don't forget to check Wikipedia for answers.
Parameterized inline SQL or parameterized stored procedures is the best way to protect yourself. As others have pointed out, simply stripping/escaping the single quote character is not enough.
You will notice that I specifically talk about "parameterized" stored procedures. Simply using a stored procedure is not enough either if you revert to concatenating the procedure's passed parameters together. In other words, wrapping the exact same vulnerable SQL statement in a stored procedure does not make it any safer. You need to use parameters in your stored procedure just like you would with inline SQL.
Also- even if you do just look for the apostrophe, you don't want to remove it. You want to escape it. You do that by replacing every apostrophe with two apostrophes.
But parameterized queries/stored procedures are so much better.
Since this a relatively older question, I wont bother writing up a complete and comprehensive answer, since most aspects of that answer have been mentioned here by one poster or another.
I do find it necessary, however, to bring up another issue that was not touched on by anyone here - SQL Smuggling. In certain situations, it is possible to "smuggle" the quote character ' into your query even if you tried to remove it. In fact, this may be possible even if you used proper commands, parameters, Stored Procedures, etc.
Check out the full research paper at http://www.comsecglobal.com/FrameWork/Upload/SQL_Smuggling.pdf (disclosure, I was the primary researcher on this) or just google "SQL Smuggling".
. . . uh about 50000000 other ways
maybe somthing like 5; drop table employees; --
resulting sql may be something like:
select * from somewhere where number = 5; drop table employees; -- and sadfsf
(-- starts a comment)
Yes, absolutely: depending on your SQL dialect and such, there are many ways to achieve injection that do not use the apostrophe.
The only reliable defense against SQL injection attacks is using the parameterized SQL statement support offered by your database interface.
Rather that trying to figure out which characters to filter out, I'd stick to parametrized queries instead, and remove the problem entirely.
It depends on how you put together the query, but in essence yes.
For example, in Java if you were to do this (deliberately egregious example):
String query = "SELECT name_ from Customer WHERE ID = " + request.getParameter("id");
then there's a good chance you are opening yourself up to an injection attack.
Java has some useful tools to protect against these, such as PreparedStatements (where you pass in a string like "SELECT name_ from Customer WHERE ID = ?" and the JDBC layer handles escapes while replacing the ? tokens for you), but some other languages are not so helpful for this.
Thing is apostrophe's maybe genuine input and you have to escape them by doubling them up when you are using inline SQL in your code. What you are looking for is a regex pattern like:
\;.*--\
A semi colon used to prematurely end the genuine statement, some injected SQL followed by a double hyphen to comment out the trailing SQL from the original genuine statement. The hyphens may be omitted in the attack.
Therefore the answer is: No, simply removing apostrophes does not gaurantee you safety from SQL Injection.
I can only repeat what others have said. Parametrized SQL is the way to go. Sure, it is a bit of a pain in the butt coding it - but once you have done it once, then it isn't difficult to cut and paste that code, and making the modifications you need. We have a lot of .Net applications that allow web site visitors specify a whole range of search criteria, and the code builds the SQL Select statement on the fly - but everything that could have been entered by a user goes into a parameter.
When you are expecting a numeric parameter, you should always be validating the input to make sure it's numeric. Beyond helping to protect against injection, the validation step will make the app more user friendly.
If you ever receive id = "hello" when you expected id = 1044, it's always better to return a useful error to the user instead of letting the database return an error.