Does sqlCommand.Parameters.Add(sqlParam) checks reserve words such as 'Table', 'Drop' etc.
Basically i want to know by using above how we can avoid Sql Injection what is the mechanism over there.
It all depends on what you plan to do with the parameters in the SQL you are executing. The good thing about using .Parameters.Add() is that the values are passed seperately and not part of 1 big chunk-o-sql. Off course it's up to you to decide what to do with them then.
Assuming you do something like this:
SELECT * FROM myTable WHERE customer_nr = #customer_nr
Then it doesn't really matter if a 'hacker' passed something along the lines of ';DROP TABLE myTable --. The query will simply not return anything because no customer is named `';DROP TABLE myTable --'
However, if you're going to use it like this:
SELECT #sql = 'SELECT * FROM myTable WHERE customer_nr = ''' + #customer_nr + ''''
EXEC (#sql)
then you defeat the purpose of the system and the hacker WILL be able to do some SQL-Injection-ish stuff.
No it doesn't treat parameters as reserve words. Using parametrized stored procedures is best way to avoid sql injection.
Related
Procedure FunctionX, Line 345
Invalid use of a side-effecting operator 'EXECUTE STRING' within a
function.
I get the above error when I execute a dynamic statement inside a function in SQL Server 2012.
Is there a workaround for this? Any tricks?
PS: The sproc (stored procedure) is much too lengthy for its body to be inserted as-is inside the function.
DECLARE #execsql NVARCHAR(2000)
Set #execsql = 'INSERT INTO #TABLE1 EXEC SPROC1 ' + #ID_COMPANY + ',' + #ID_COUNTRY
exec (#execsql)
Many thanks in advance.
Also, I need to be able to delete inside the function as well. I know this contradicts the definition of functions but I am wondering if there are some tricks that can be used
No there are no tricks, see The Curse and Blessings of Dynamic SQL
Dynamic SQL in User-Defined Functions
This is very simple: you cannot use dynamic SQL from used-defined
functions written in T-SQL. This is because you are not permitted to do
anything in a UDF that could change the database state (as the UDF may
be invoked as part of a query). Since you can do anything from dynamic
SQL, including updates, it is obvious why dynamic SQL is not
permitted.
I've seen more than one post on the newsgroups where people have been
banging their head against this. But if you want to use dynamic SQL in
a UDF, back out and redo your design. You have hit a roadblock, and in
SQL 2000 there is no way out.
In SQL 2005 and later, you could implement your function as a CLR
function. Recall that all data access from the CLR is dynamic SQL.
(You are safe-guarded, so that if you perform an update operation from
your function, you will get caught.) A word of warning though: data
access from scalar UDFs can often give performance problems. If you
say
SELECT ... FROM tbl WHERE dbo.MyUdf(somecol) = #value
and MyUdf performs data access, you have more or less created a hidden
cursor.
I was having this same problem with dynamic OPENQUERY statements inside a multi-line table-valued function. SQL Server is trying to prevent users with only db_datareader access, who can select from these functions, from performing SQL injections. Long story short, remove as many single quotes as you can and find a way to do the same thing without using EXEC.
Instead of doing this:
Set #execsql = 'INSERT INTO #TABLE1 EXEC SPROC1 ' + #ID_COMPANY + ',' + #ID_COUNTRY
Do something like this:
INSERT INTO #TABLE1
SELECT *
FROM --some unfiltered version of the table your stored procedure uses
WHERE company = #ID_COMPANY
AND country = #ID_COUNTRY
Since you're calling a function from a stored procedure you can already be sure the table will be up to date. In my case, I was able to have a job refresh the function's underlying table using the stored procedure once every morning. You could also use a trigger to do that.
I have a large dynamic SQL statement that I need to execute on multiple databases and potentially on multiple servers in the future.
At the start of the dynamic SQL I can use
USE Database1
and everything works fine. But I was wondering if there was a way I could specify the server name for linked servers? For example
USE Server1.Database1
I'm aware I could use fully qualified names in queries, this however make the sql harder to read. Is there a way I can avoid fully qualified names?
To summarise what I'm trying to achieve - I have a central database with a stored procedure that fetches and processes data from multiple databases into one location so that the client can easily report on it.
I would consider using sqlcmd, instead, which is intended for this purpose. You would specify your variable at the top of the script like this:
:setvar MyLinkedServer "MyLinkedServerName"
:setvar DatabaseName "MyDb"
Then call it in the script like this:
[$(MyLinkedServer)].[$(DatabaseName)].SomeSchema.SomeTable
Before the script is executed sqlcmd will replace the values, even if it is in a text string, as would be the case in your dynamic sql. The variables can also be set as a parameter to the script file by using sqlcmd.exe to execute the script. This should help keep the code looking a little cleaner.
Since you mentioned readability as a reason to not just simply use fully qualified names, and also mentioned that you aren't familiar with aliasing, I'm left to assume your query looks like this.
select
serverA.Database1.dbo.TableName.ColumnExample,
serverB.Database2.dbo.TableName.SecondExample
from serverA.Database1.dbo.TableName
inner join serverB.Database2.dbo.TableName
on serverA.Database1.dbo.TableName.BlahID = serverB.Database2.dbo.TableName.BlergID
And if this is the case then yes this sucks on ice and is very unreadable. So what you can do is add aliases to clean this code up so you have a query that looks like this.
select
A.ColumnExample,
B.SecondExample
from serverA.Database1.dbo.TableName as A --By setting the Alias as A you can now
inner join serverB.Database2.dbo.TableName as B --reference that instead of
on A.BlahID = B.BlergID --qualifying the whole statement
This would allow it to be much simpler to build a readable dynamic SQL statement that calls many databases on many servers. Then simply use whatever method you are using to call your dynamic SQL statement as
N'select
A.ColumnExample,
B.SecondExample
from ' + #Server1 + #Database1 + '.dbo.TableName as A --By setting the Alias as A you can now
inner join ' + #Server2 + #Database2 + '.dbo.TableName as B --reference that instead of
on A.BlahID = B.BlergID --qualifying the whole statement'
I would recommend adding the .prior to the database in the server variable, that way you can leave it off if it's a local server.
If it is one and the same query that just needs to be executed in different contexts, you could try specifying the context by calling the corresponding instance of sp_executesql to execute your dynamic query:
linkedserver.remotedatabase.sys.sp_executesql N'
SELECT
...
FROM dbo.Table1 AS a
INNER JOIN dbo.Table2 AS b ON ...
';
That way linkedserver.remotedatabase would be the current database and indeed linkedserver the current server of the specified query.
That method, however, might require an additional level of dynamicity since you are reading the names from a table. For the following illustration of how the query building part would look like in such a case, I am assuming that the names of the server and the database are stored in variables (as if, for instance, you have populated the vars in a cursor iteration):
DECLARE #sql nvarchar(max), #metasql nvarchar(max);
SET #sql = N'your dynamic query (without USE)';
SET #metasql = QUOTENAME(#servername) + N'.' + QUOTENAME(#databasename) + N'.sys.sp_execute #sql';
EXECUTE sp_execute #metasql, N'#sql nvarchar(max)', #sql;
This kind of double nesting may not be very good for the occasional debugging you are mentioning in one of your comments, though. But then you could go with incorporation of the #sql's contents into #metasql instead of parametrisation:
DECLARE #sql nvarchar(max), #metasql nvarchar(max);
SET #sql = N'your dynamic query (without USE)';
SET #metasql = QUOTENAME(#servername) + N'.' + QUOTENAME(#databasename) + N'.sys.sp_execute N' + QUOTENAME(#sql, '''');
EXECUTE sp_execute #metasql;
I found strange rules in MS SQL CREATE VIEW syntax.
It must be on the first line of query batch processing and it must be created in the current database.
I should make VIEWs that have dynamic name described by string variables
(type: VARCHAR or NVARCHAR). And those VIEWs should be created in other databases.
Because of the rule, CREATE VIEW statement must be on the first line of query batch processing, it cannot be after USE statement.
So, I tried to change databases with USE & GO statement. But GO statement seemed to make clear all the variables. Therefore they are not available that describe VIEW name after GO statement.
Do you have any opinon for me?
And if you know the reasons of CREATE VIEW syntax rules, please tell me.
Oh~, Sorry. I missed one thing. The names of databases are also dynamic.
And VIEWs, I want to make, not only should access tables of other databases
but also shoule be created in other databases.
Though I don't know OLAP well, I think this situation is involved OLAP.
You are able to dynamically create a sql-string and execute it.
DECLARE #ViewName VARCHAR(100)
SET #ViewName = 'MyView'
USE MyDB;
EXEC ( 'CREATE VIEW dbo.' + #ViewName + ' '
+ 'AS SELECT * FROM dbo.MyTable')
CREATE SYNONYM Resource1 FOR LinkedServer.Database.Schema.Table
GO
CREATE VIEW Resource1View
AS
SELECT *
FROM Resource1
GO
Now you can alter the synonym as much as you like and all your views referencing it will refer to the correct thing. If this doesn't solve the problem, then I would suggest that the way you're designing your system is not best. Please describe more what you are doing and why so we can advise you better.
As for "GO", is it actually not a SQL statement. It is never submitted to the server. The client sees the line with GO on it, and separates the submitted query into separate batches. A trace will prove this, as will EXEC 'SELECT 1' + CHAR(13) + CHAR(10) + 'GO' + CHAR(13) + CHAR(10) + 'SELECT 2'.
If you're using OLAP as in Analysis Services, then I'm not experienced enough with that to help you, but I would think there'd be ways to choose the database to connect to just like in SSRS, and that queries don't have to live in the database but could live in the SSAS application.
I found it. It's the nested EXEC.
I want to write a stored proc which will use a parameter, which will be the table name.
E.g:
#tablename << Parameter
SELECT * FROM #tablename
How is this possible?
I wrote this:
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[GetAllInterviewQuestions]
#Alias varchar = null
AS
BEGIN
Exec('Select * FROM Table as ' #Alias)
END
But it says incorrect syntax near #Alias.
Well, firstly you've omitted the '+' from your string. This way of doing things is far from ideal, but you can do
DECLARE #SQL nvarchar(max)
SELECT #SQL = 'SELECT * FROM ' + QuoteName(#Alias)
Exec(#SQL)
I'd strongly suggest rethinking how you do this, however. Generating Dynamic SQL often leads to SQL Injection vulnerabilities as well as making it harder for SQL Server (and other DBs) to work out the best way to process your query. If you have a stored procedure that can return any table, you're really getting virtually no benefit from it being a stored procedure in the first place as it won't be able to do much in the way of optimizations, and you're largely emasculating the security benefits too.
You'll have to do it like this:
exec('select * from '+#tablename+' where...')
But make sure you fully understand the risks, like SQL injection attacks. In general, you shouldn't ever have to use something like this if the DB is well designed.
Don't you mean
Exec('SELECT * FROM ' + #tableName)
Also, the error you get is because you've forgotten a + before #Alias.
Often, having to parameterize the table name indicates you should re-think your database schema. If you are pulling interview questions from many different tables, it is probably better to create one table with a column distinguishing between the questions in whatever way the different tables would have.
Most implementations of SQL do not allow you to specify structural elements - table names, column names, order by columns, etc. - via parameters; you have to use dynamic SQL to parameterize those aspects of a query.
However, looking at the SQL, you have:
Exec('SELECT * FROM Table AS ' #Alias)
Surely, this would mean that the code will only ever select from a table called 'Table', and you would need to concatenate the #Alias with it -- and in many SQL dialects, concatenation is indicated by '||':
Exec('SELECT * FROM Table AS ' || #Alias)
This still probably doesn't do what you want - but it might not generate a syntax error when the procedure is created (but it would probably generate an error at runtime).
simple problem, but perhaps no simple solution, at least I can't think of one of the top of my head but then I'm not the best at finding the best solutions.
I have a stored proc, this stored proc does (in a basic form) a select on a table, envision this:
SELECT * FROM myTable
okay, simple enough, except the table name it needs to search on isn't known, so we ended up with something pretty similiar to this:
-- Just to give some context to the variables I'll be using
DECLARE #metaInfoID AS INT
SET #metaInfoID = 1
DECLARE #metaInfoTable AS VARCHAR(200)
SELECT #metaInfoTable = MetaInfoTableName FROM MetaInfos WHERE MetaInfoID = #MetaInfoID
DECLARE #sql AS VARCHAR(200)
SET #sql = 'SELECT * FROM ' + #metaInfoTable
EXEC #sql
So, I, recognize this is ultimately bad, and can see immediately where I can perform a sql injection attack. So, the question is, is there a way to achieve the same results without the construction of the dynamic sql? or am I going to have to be super, super careful in my client code?
You have to use dynamic sql if you don't know the table name up front. But yes, you should validate the value before attempting to use it in an SQL statement.
e.g.
IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=#metaInfoTable)
BEGIN
-- Execute the SELECT * FROM #metaInfoTable dynamic sql
END
This will make sure a table with that name exists. There is an overhead to doing this obviously as you're querying INFORMATION_SCHEMA. You could instead validate the #metaInfoTable contains only certain characters:
-- only run dynamic sql if table name value contains 0-9,a-z,A-Z, underscores or spaces (enclose table name in square brackets, in case it does contain spaces)
IF NOT #metaInfoTable LIKE '%^[0-9a-zA-Z_ ]%'
BEGIN
-- Execute the SELECT * FROM #metaInfoTable dynamic sql
END
Given the constraints described, I'd suggest 2 ways, with slight variations in performance an architecture.
Choose At the Client & Re-Architect
I'd suggest that you should consider a small re-architecture as much as possible to force the caller/client to decide which table to get its data from. It's a code smell to hold table names in another table.
I am taking an assumption here that #MetaInfoID is being passed from a webapp, data access block, etc. That's where the logic of which table to perform the SELECT on should be housed. I'd say that the client should know which stored procedure (GetCustomers or GetProducts) to call based on that #MetaInfoID. Create new method in your DAL like GetCustomersMetaInfo() and GetProductsMetaInfo() and GetInvoicesMetaInfo() which call into their appropriate sprocs (with no dynamic SQL needed, and no maintenance of a meta table in the DB).
Perhaps try to re-architect the system a little bit.
In SQL Server
If you absolutely have to do this lookup in the DB, and depending on the number of tables that you have, you could perform a handful of IF statements (as many as needed) like:
IF #MetaInfoID = 1
SELECT * FROM Customers
IF #MetaInfoID =2
SELECT * FROM Products
-- etc
That would probably become to be a nightmare to maintain.
Perhaps you could write a stored procedure for each MetaInfo. In this way, you gain the advantage of pre-compilation, and no SQL injection can occur here. (imagine if someone sabotaged the MetaInfoTableName column)
IF #MetaInfoID = 1
EXEC GetAllCustomers
IF #MetaInfoID = 2
EXEC GetAllProducts