I've got the SQL stored procedure from hell that I've created and all input parameters are parameterised for security but it's not running as quick as I'd like so I wanted to make it dynamic and so a bit more efficient.
I know I can keep my input parameters to my stored procedure, then within it create a dynamic SQL statement into which I can then pass the input parameters of the stored procedure, but are there any security implications I need to be aware of when doing this? I'm guessing not as it just another set of parameters and they should be treated the same as the parameters passed to the current stored procedure.
Obviously, producing code like this "WHERE OrderNo = ' + #orderno is asking for trouble - I will be doing 'WHERE OrderNo = #orderno' in the dynamic SQL, but is there anything else I need to be aware of?
Thx MH
PS - before anyone suggests it, I can't create the SQL dynamically at the client side using LINQ or similar - it all (for various reasons) has to be contained and controlled at the database level
There is a form of SQL injection that many people don't think about when doing dynamic SQL in stored procedures: SQL Truncation attacks.
With a SQL truncation attack, the attacker injects a long peace of text making the used text variable overflow and lose part of the query.
This article gives more information about this.
Where your parameters are always Data Items, both when being passed to the StoredProc and when used in yor DynamicSQL, everything will stay safe.
Should any of your StoredProc's parameters end up being table or field names, and so forming part of the structure of the DynamicSQL itself, you introduce a new risk : That the parameter can be used to inject rogue SQL Code.
To prevent against such an injection attack you should always validate any such parameters.
One example of how to do this would be to use the input parameter as a token, rather than substitute it directly into the DynamicSQL...
SET #SQL = #SLQ + CASE targetTable WHEN '1' THEN 'table1'
WHEN 'tx' THEN 'tableX'
END
Some people suggest you only need to validate on the client application. But that means that if someone becomes able to execute you SP's directly, the SP has become a point of attack. I always prefer to validate both on the client AND in the server.
EDIT Performance
Note that using DynamicSQL isn't always a guarnatee of performance increases. If you use parameterised queries, the execution plans can indeed be stored. But if the queries do vary greatly, you may still find a significant overhead in compiling the SQL.
There is also the fact that dependancy tracking is lost. It's not possible to see what tables the SP is dependant on, because the code is hidden away as strings.
I have very rarely found that DynamicSQL is needed. Often a complex query can be reformed as several optimised queries. Or the data can be re-structured to meet the new demands. Or even a rethink of both the data and the algorithm using the data. One might even be able to suggest that a dependancy on DynamicSQL is an indicator of another underlying problem.
Perhaps it's not in the scope of your question, but it would be interesting to see the actual puzzle you're facing; to see if anyone has any alternative approaches for you.
Related
I have some hard coded database values in my SQL and I need to convert to variables , I have declared them in places but I need Production2 to be changed to #Source_Database_Name variable below but I dont know how to place it in with the Information Schema just after it without getting a syntax error
IF EXISTS(SELECT * FROM Production2.INFORMATION_SCHEMA.COLUMNS
I guess that the only way you can do this is dynamic sql generation (unfortunately). And there's actually quite a few reasons (from database engine's perspective) for not allowing a user to parametrise queries in a way you want. The one that sits on top of my head is that it will make impossible to validate syntax of your query (no way to know that you're referring to what actually exists).
In case you're talking about "being able to execute the same set of SQL against different database(s)" and you're actually executing this sql from code (.NET / anything), you can achieve the same result by specifying target database in connection string (i.e. by changing the level where you set database -- not in the [sql] script, but rather at some external point).
I'm getting a little confused about using parameters with SQL queries, and seeing some things that I can't immediately explain, so I'm just after some background info at this point.
First, is there a standard format for parameter names in queries, or is this database/middleware dependent ? I've seen both this:-
DELETE * FROM #tablename
and...
DELETE * FROM :tablename
Second - where (typically) does the parameter replacement happen? Are parameters replaced/expanded before the query is sent to the database, or does the database receive params and query separately, and perform the expansion itself?
Just as background, I'm using the DevArt UniDAC toolkit from a C++Builder app to connect via ODBC to an Excel spreadsheet. I know this is almost pessimal in a few ways... (I'm trying to understand why a particular command works only when it doesn't use parameters)
With such data access libraries, like UniDAC or FireDAC, you can use macros. They allow you to use special markers (called macro) in the places of a SQL command, where parameter are disallowed. I dont know UniDAC API, but will provide a sample for FireDAC:
ADQuery1.SQL.Text := 'DELETE * FROM &tablename';
ADQuery1.MacroByName('tablename').AsRaw := 'MyTab';
ADQuery1.ExecSQL;
Second - where (typically) does the parameter replacement happen?
It doesn't. That's the whole point. Data elements in your query stay data items. Code elements stay code elements. The two never intersect, and thus there is never an opportunity for malicious data to be treated as code.
connect via ODBC to an Excel spreadsheet... I'm trying to understand why a particular command works only when it doesn't use parameters
Excel isn't really a database engine, but if it were, you still can't use a parameter for the name a table.
SQL parameters are sent to the database. The database performs the expansion itself. That allows the database to set up a query plan that will work for different values of the parameters.
Microsoft always uses #parname for parameters. Oracle uses :parname. Other databases are different.
No database I know of allows you to specify the table name as a parameter. You have to expand that client side, like:
command.CommandText = string.Format("DELETE FROM {0}", tableName);
P.S. A * is not allowed after a DELETE. After all, you can only delete whole rows, not a set of columns.
I am using mysql and trying to block unwanted queries injection of people who will try to use my single query to run several ones. ie, for example when i have the parameter "?id=3", people can try to run it with ="id=3;drop table users"
Now, i know that the best way to avoid this is by parsing and checking the parameter, but is there a way to change the concatenated queries delimiter from ";" to something like "%^#$%##$^$"?
Security through obscurity is useless. Take the time to write the proper code to protect against the SQL injection attacks. Doing it up front will cost you a lot less than doing it after you've had a successful attack run against your code!
The best way to defend against injection attacks is to use Prepared Statements.
By using Prepared Statements, you are immune to most injection attacks (which of course aren't the only security vulnerability you need to think about, but they're a pretty major one.)
The statement DELIMITER configuration is a built-in command only in the mysql client tool. You can't change the delimiter for multi-statements. It's always semicolon.
Also, the MySQL API allows execution of only one statement at a time, by default. The example you're talking about doesn't work unless you explicitly enable multi-statements.
Furthermore, multi-statements isn't the only vector for SQL injection. Even if you could change the statement delimiter, it wouldn't do anything to protect against SQL injection that modifies a single given statement.
UPDATE Accounts SET PASSWORD = '...' WHERE account_id = $id
In this example, if $id has a value of "1234 OR 1=1" then the attacker has changed the password for all accounts, including probably a privileged user. And yet no multi-statements were involved.
You still need to be mindful of security issues when you write code. There's no silver bullet to protect against SQL injection.
Even query parameters aren't a cure-all for SQL injection. Parameters take the place only of values in SQL expressions. There are many common cases where it's still necessary to interpolate application variables into an SQL string. For example, when parameterizing an IN() predicate, or when choosing ORDER BY expressions. Don't listen to people who say prepared queries are 100% proof against security flaws.
See also my presentation SQL Injection Myths and Fallacies, or the chapter on SQL Injection in my book, SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
When you call mysql_query or mysql_real_query, it won't run multiple statements anyway, so the statement delimiter doesn't really matter. You can enable multiple statements per query when you connect, but since you're trying to avoid that ability, simply don't enable it.
An even better option for avoid SQL injection is to use prepared statements. Start with mysql_stmt_init and mysql_stmt_prepare with placeholders for your statement's parameters, and then fill in the parameters with mysql_stmt_bind_param before mysql_stmt_execute. If you're not calling the API directly, then whatever wrapper library you have should also provide support for prepared statements. (If it doesn't support them, then consider switching to a better wrapper.)
I am building a series of web services in VB.Net
Each of the web services takes multiple string values, performs some validation/processing then calls a Stored Procedure using Linq to SQL. Some of the string contains user data that is stored in the database:
These string values passed from the web service are escaped to trap single quote characters, semi colons and the various bracket types.
I am calling the SP using the datacontext.spname(parameter1, parameter2) method.
The aim is to ensure that the web services are as resilient as possible, while still being performant.
Have I done enough to prevent SQL injection attacks?
Usually you are good, but there are a few caveats:
Careful of stored procs that use sp_executesql or exec. You can pass in a query in the param and end up executing it.
Careful with LIKE sections of queries cause they can be widened with % if likened to a param.
Fields used in webpages may need some extra processing before being sent in, to avoid cross site scripting. (which you should also defend against when pulling information out)
I know for a fact that LINQ to SQL queries all the data send to the database via SQL parameters -- which keeps you safe from SQL injection. I'm not entirely sure, but since LINQ abstracts the stored procedure, it too most likely passes the arguments to the stored procedures in the same manner.
What does that mean? You don't have to worry about sanitizing your data because LINQ will take care of it. You could of course test it out with a simple SQL injection type attack -- something like a harmless insert or select.
If you're using parameters then you don't need to sanitise at all as single quotes and the other sql injection nasties get escaped for you.
It's probably a bad idea to sanitise on input depending on the data you're storing. If you're storing things that end up embedded in a web page and you encode/sanitise them on data entry what happens if your sanitation code has a bug? You end up with data in the database that will cause problems on output and no easy way to fix it without an update over all your data. It's better to sanitise when you output data as corrections to the sanitation code will then run against all data. You also have the advantage of easier searching in SQL should that be a concern.
I'd limit the web service to obvious things, null and range checks.
As the topic suggests I wish to be able to pass table names as parameters using .NET (doesn't matter which language really) and SQL Server.
I know how to do this for values, e.g. command.Parameters.AddWithValue("whatever", whatever) using #whatever in the query to denote the parameter. The thing is I am in a situation where I wish to be able to do this with other parts of the query such as column and table names.
This is not an ideal situation but it's one I have to use, it's not really prone to SQL injection as only someone using the code can set these table names and not the end-user. It is messy however.
So, is what I am asking possible?
EDIT: To make the point about SQL injection clear, the table names are only passed in by source code, depending on the situation. It is the developer who specifies this. The developer will have access to the database layer anyway, so the reason I am asking is not so much for security but just to make the code cleaner.
You cannot directly parameterize the table name. You can do it indirectly via sp_ExecuteSQL, but you might just as well build the (parameterized) TSQL in C# (concatenating the table-name but not the other values) and send it down as a command. You get the same security model (i.e. you need explicit SELECT etc, and assuming it isn't signed etc).
Also - be sure to white-list the table name.
I don't think I've ever seen this capability in any SQL dialect I've seen, but it's not an area of expertise.
I would suggest restricting the characters to A-Z, a-z, 0-9, '.', '_' and ' ' - and then use whatever the appropriate bracketing is for the database (e.g. [] for SQL Server, I believe) to wrap round the whole thing. Then just place it directly in the SQL.
It's not entirely clear what you meant about it not being a SQL injection risk - do you mean the names will be in source code and only in source code? If so, I agree that makes things better. You may not even need to do the bracketing automatically, if you trust your developers not to be cretins (deliberately or not).
You can pass the table name as a parameter like any other parameter. the key is you have to build a dynamic sql statement, which then you should consider if it's easier to build it in your app tier or in the procs.
create procedure myProc
#tableName nvarchar(50)
as
sp_executesql N'select * from ' + #tablename
fyi this code sample is from memory have a look at BOL for the proper syntax of sp_executesql.
Also this is highly sucesptible to SQL injection as you indicated is not an issue for you but anyone reading this should be very wary of accepting input from a user to generate their queries like this.
SQL query parameters can only take the place of a literal value. You cannot use a parameter for a table name, column name, list of values, or other SQL syntax. That's standard SQL behavior across all brands of database.
The only way to make the table name dynamic is to interpolate a variable into your SQL query before you prepare that string as a statement.
BTW, you're fooling yourself if you think this isn't a risk for SQL injection. If you interpolate the table name into the query dynamically, you need to use delimited identifiers around the table name, just as you would use quotes around a string literal that is interpolated from a variable.
The idea that it is not prone to SQL injection is misguided. It may be less prone to SQL injection from front end users, but it is still very much prone to SQL injection. Most attacks on databases come from inside the company being attacked, not from end users.
Employees may have grudges, they may be dishonest, they may be disgruntled, or they may just be not so bright and think that it's ok to bypass security to do whatever it is that THEY think should be done to the database.
Please see this post answer by user Vimvq1987:
MySqlParameter as TableName
Essentially you first check the table name against the schema, in which the table name is used in a parameterized fashion. Then if all is ok, the table name is legit.
Paraphrased basic idea is:
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'databasename'
AND table_name = #table;
cmd.Parameters.AddWithValue("#table",TableName);
If this returns ok with the table name, go ahead with your main query...
I would just check
select OBJECT_ID(#tablename)
the idea is to prevent injection you know it has to be table name this was if this returns a number then i would run the actual query,