Prevent update/delete in stored procedure having dynamic queries - sql

There's one stored procedure which executes a dynamic queries which will be from parameter. The dynamic queries mostly contains just SELECT statement but may contain delete/update some time.
I want to raise errors when this procedure tries to run the dynamic query which has DELETE or UPDATE commands.
Eg..
create procedure spa_test
#sql varchar(2000)
AS
EXEC(#sql)
go
----------------------
SET #sql1 = ' DELETE FROM data_table where .... ;
.........
SELECT * FROM some_table .. '
SET #sql2 = '....
.........
SELECT * FROM some_table '
EXEC spa_test #sql1 -- should result in error
EXEC spa_test #sql2 -- no error and procedure runs successfully
Can I accomplish this by using SQL Server Policy management ?
I can not alter this procedure spa_test and can not change the privilege of user running it.
Is there any other way I can accomplish this need ?

You can use CHARINDEX() to search for DELETE , UPDATE expression in your #sql query.
CHARINDEX ( expressionToFind ,expressionToSearch)
-
IF (CHARINDEX(#sql1 , 'DELETE FROM') <> 0 or CHARINDEX(#sql1 , 'UPDATE') <> 0 )
RAISERROR(...)
ELSE
EXEC(#sql1)

Related

Dynamically run SQL queries

I want to run SQL queries which retrieve from a database according to user input.
Lets assume there is a table named Queries and the fields of the table are Index, Query, Name.
query - select * from Student
name - GetStudents
Index - 1
When user clicks a button an index will be passed into the server and a query match with that index will be triggered. Assume there are no inputs into queries.
Lets say there are 5 rows in the table and when user pass 3,the third query will be run. When user pass 4 the fourth query will be run.
I think I can simply do this by storing the query as a string on table retrieving the query and run. But I'm not sure whether this workaround is efficient.
Please help me with these points.
Is this approach is okay or is there any better workaround that I can follow.
Is it okay to store query as a string in a table.
Is there any workaround that I can create Stored Procedures pragmatically using asp.net in SQL server management studio.
I'm using ASP.Net and SQL server.
Note that here I can't use Stored Procedures to do this task. Because there is another front-end where user can insert queries into table that I have mentioned above. User has no access to use SQL server management studio.
In theory, yes you certainly could store the query string and then use sp_executesql to run that particular query string.
However, CAUTION. If you have a front end that allows a user to write and submit a query then how are you sanitizing that input? Is there anything to prevent the user submitting 'DROP DATABASE' as the query or event introducing other SQL injection attacks?
A better approach would be to create the procedures (assuming that the activities are all standard tasks) and allowing the user to select which procedure to execute.
You could check the integrity of the dynamic select statement by executing it under the most restrictive security context (a dbuser that has readonly permissions). To take it a bit further, you could also wrap the dynamic select statement into an ever changing dynamic container/string (ever changing part + dynamic query + ever changing part) and suppress any errors that happen during the validation/integrity check.
You cannot rely solely on sanitization because you'll end up in a never-ending catching-up struggle.
use mydbxyz;
go
--create a readonly dbuser
create user readonlydbuser without login;
alter role db_datareader add member readonlydbuser;
alter role db_denydatawriter add member readonlydbuser;
go
--procedure to execute dynamic select (no cte, no variables, just selects)
create or alter procedure execute_simpleselect #sqlinput nvarchar(max) = null
with execute as owner
as
begin
set nocount on;
if nullif(#sqlinput, '') is null
begin
--nothing to execute
return;
end
--check if sql input is a valid/simple select query
declare #foocontrol tinyint;
declare #tblalias sysname = quotename(newid());
declare #sqlcheck nvarchar(max) = N'
select #var = 1;
begin transaction;
begin try
select top (0) #var = '+ #tblalias + '.mycol
from
(
select 1 as mycol
where exists
(
'
+ #sqlinput +
'
)
) as '+ #tblalias + N'
end try
begin catch
select #var = 2;
end catch
rollback transaction;
';
/*
create user readonlydbuser without login;
alter role db_datareader add member readonlydbuser;
alter role db_denydatawriter add member readonlydbuser;
*/
--catch errors
begin try
--change context to a readonlyuser
execute as user='readonlydbuser'; --if this dbuser does not exist, nothing executes
exec sys.sp_executesql #stmt = #sqlcheck, #params = N'#var tinyint output', #var = #foocontrol output;
end try
begin catch
--do nothing, suppress errors
end catch
revert;
--if #foocontrol is not 1, the query cannnot be executed (syntactically incorrect, violation under minimal permissions etc)
if isnull(#foocontrol, 2) = 2
begin
raiserror('what are you trying to do?', 16, 1);
return;
end
--change to the callers security context
exec as caller;
exec sys.sp_executesql #stmt = #sqlinput;
end
--test
exec execute_simpleselect #sqlinput = 'select * from sys.objects';
exec execute_simpleselect #sqlinput = 'create table dbo.testtbl(id int)';
exec execute_simpleselect #sqlinput = 'drop table dbo.tablexzy';
exec execute_simpleselect #sqlinput = 'select user_name()';

Concatenated SQL statement

I'm working on a query that builds a list of table names from the sys.database master table. I then use those name in a concat statement to pull a specific piece if information out of each database. I'm running the whole thing through a while loop to hit all tables, with a counter.
i.e.
set #sql = 'select top 10 * from ' + (select dbname from #table where tabid = #i + '.dbo.**tablename** where NAME = '**String Value**'
When I just print the results of #sql, it gives me the correct syntax, and I am able to run the command with no problem.
When I set #sql to exec, I get an error
"database select top 10 * from dbname does not exist."
It's like the execute is ignoring everything after the select.
I'm suspecting you're calling EXEC incorrectly.
When calling 'exec' with a T-SQL query, be sure to enclose the target string in parenthesis, eg
DECLARE #FOO VARCHAR(100)
Set #FOO = 'SELECT TOP 5 * from SOMETABLE'
EXEC (#FOO) -- not EXEC #FOO

SQL Server 2008: Insert variable into DML statements using Stored Procedure

I have the following procedure:
CREATE PROCEDURE [dbo].[Test1]
AS
BEGIN
INSERT INTO [My_Database].[My_Schema].[My_Table]
(...lists columns...)
SELECT ... lots of columns from joined query...
END
Instead of hardcoding "[My_Database].[My_Schema]", I now want to select it as a variable from a predefined table like this:
CREATE PROCEDURE [dbo].[Test1]
AS
BEGIN
SELECT #myDB = [My_DB] FROM [my_custom_table]
--INSERT INTO [My_Database].[My_Schema].[My_Table]
INSERT INTO #myDB.[My_Table]
(...lists columns...)
SELECT ... lots of columns from joined query...
END
It does not work if I use it like above. I need to use:
EXEC sp_executesql (entire_sql_statement_in_quotes)
My problem is that I have a lot of these procedures to change to using a variable instead of being hardcoded. It will take forever to convert each statement to a long string.
Is there some other way to do it? What am I missing?
Regards
One idea, you could drop and recreate a synonym using dynamic SQL at the beginning of each procedure, then you can leave each Insert statement as Insert Into MySynonym
DROP SYNONYM MySynonym -- Must create it first before running this bit!
DECLARE #sql nvarchar(max)
SET #SQL = 'CREATE SYNONYM MySynonym
FOR ' + #myDB + '.test1'
EXEC sp_Executesql #sql
INSERT INTO MySynonym
SELECT ...
This would give you a peice of code you could copy paste into each SP. If the table you are inserting into is different for each SP, you could declare that too and build it into your CREATE SYNONYM statement
SET #SQL = 'CREATE SYNONYM MySynonym
FOR ' + #myDB + '.' + #MyTable
to Truncate each table first you would need to use DynamicSQL also, as you cannot delete on a synonym
SET #SQL = 'Truncate Table ' + #MyTable
EXEC sp_Executesql #sql

execute stored procedures returned from database table

I am working with sql server 2008
I have a database table that has a column containing a stored procedure name.
I want to query the database table which returns a list of the stored procedure names, and execute them.
The stored procedures are similar all having a select statment. The data returned in this select statement I want to insert in to a data base table.
Pseudo code looks like this:
INSERT INTO MyTable
EXECUTE sp_executesql SELECT StoredProcedureName FROM Table
Anyone able to assist me with correct sql for achieveing the above?
sp_executesql accepts a unicode string not a tsql statement. So you would need to execute your procedure(s) like this:
execute sp_executesql 'execute ' + #storedprocedurename
which will execute a single procedure.
You will need to write some iterative process to populate the #storedprocedurename variable from your source table.
This is pretty much same as #Coltech answer just with cursor.
DECLARE #spname VARCHAR(200)
DECLARE #sql VARCHAR(1000)
DECLARE your_cursor CURSOR FOR
SELECT spname
FROM yourTable;
OPEN your_cursor;
FETCH NEXT FROM your_cursor
INTO #spname;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'EXEC ' + #spname
execute sp_executesql #sql
FETCH NEXT FROM your_cursor
INTO #spname;
END
CLOSE your_cursor;

Execute stored proc with OPENQUERY

I have SQL Server 2008 with a linked Sybase server and I am trying to execute a stored procedure on the Sybase server using OPENQUERY. If I have a stored proc that doesn't take parameters it succeeds fine. If I have a stored proc with parameters it fails. I even tried a very basic stored proc that only took an int an that still failed. Below is the syntax I am using:
select * from
OPENQUERY([LINKSERVER],'exec database.user.my_stored_proc ''AT'',''XXXX%'',''1111'',1')
Msg 7357, Level 16, State 2, Line 3
Cannot process the object "exec database.user.my_stored_proc 'AT','XXXX%','1111',1". The OLE DB provider "ASEOLEDB" for linked server "LINKSERVER" indicates that either the object has no columns or the current user does not have permissions on that object.
As the proc will execute just fine without parameters, I don't think it is a permission issue.
This worked for me,
SELECT * FROM OPENQUERY(LOCALSERVER, 'SET FMTONLY OFF EXEC snr.dbo.GetAllSignals #controlRunId = 25, #experimentRunId = 26')
I was creating temporary tables, and that's why i got access denied
Here is more info http://www.sommarskog.se/share_data.html#OPENQUERY
I create a sp that doesn't return any value and it doesn't work.
Your SP in mysql have to return a value!
for example I do this in "mysql":
CREATE DEFINER=`root`#`localhost` PROCEDURE `MyPro`(IN `Name` VARCHAR(50), IN `Id` INT, OUT `Result` INT)
MODIFIES SQL DATA
BEGIN
DECLARE Result INT;
SET Result = 0;
INSERT into MyTable (Id,Name) VALUES(Id,Name);
SELECT Result;
END
That "Id" and "Name" is input parameter and "Result" is output parameter
and create linked server in SQL SERVER and call it like this:
select * from openquery
(
Test,'call mydb.MyPro(''Name'',''16'', #P0);'
)
It works for me :D
Linked Servers and OPENQUERY, Gems to MS SQL Server...that are wolves in sheep clothing. I've found the following solutions to work when dealing with parameters
If the SP is basically just SELECT statements, the move the same to a VIEW and just pass SQL statements via OPENQUERY.
Build the OPENQUERY as a string and then use execute_sql.
You could also see if it works to precede exec with SET FMTONLY ON:
OPENQUERY([LINKSERVER],'SET FMTONLY ON; exec database.user.my_stored_proc ''AT'',''XXXX%'',''1111'',1')
If you try this and it works, you should probably Google FMTONLY+OPENQUERY to get an idea of what it means.
Try this,
SELECT * FROM OPENQUERY(linked_server_name, 'SELECT postgres_procedure_name (parameters)');
I experienced a very similar issue, but my SP wasn't taking any parameters.
I tried experimenting with altering the query sent through the openquery to include 'SET NOCOUNT ON' and 'SET FMTONLY OFF' but this had no difference.
The only solution that worked for my stored procedure was dropping the existing version, and altering the code to specifically 'SET NOCOUNT ON'
After doing this I was able to successfully run my stored proc through my linked server connection.
First of all you have to add hard code text fields then you have to
replace it by your parameters value like FromDate,TillDate,EmpID,CompCode,0,DeptID,DesgId,LocationID,AtnType
DECLARE #startdate varchar(255) = '2019-12-17'
DECLARE #enddate varchar(255) = '2019-12-17'
Set #SQL = 'SELECT * FROM OPENQUERY(' + quotename(#LinkedServer) + ',' + '''' +
'SET FMTONLY OFF; exec [TAP].[dbo].[GetAttendanceList] ' + 'FromDate,TillDate,EmpID,CompCode,0,DeptID,DesgId,LocationID,AtnType,1'')'
You have to replace your parameters values shown below
set #SQL=REPLACE(#SQL,'FromDate',+''''+''''+#startdate+''''+'''')
set #SQL=REPLACE(#SQL,'TillDate',+''''+''''+#enddate+''''+'''')
set #SQL=REPLACE(#SQL,'CompCode',+''''+''''+#CompCode+''''+'''')
set #SQL=REPLACE(#SQL,'AtnType',+''''+''''+''''+'''')
if #EmpID is Null
begin
set #SQL=REPLACE(#SQL,'EmpID','null')
end
if #DeptID is Null
begin
set #SQL=REPLACE(#SQL,'DeptID','null')
end
if #DesgId is Null
begin
set #SQL=REPLACE(#SQL,'DesgId','null')
end
if #LocationID is Null
begin
set #SQL=REPLACE(#SQL,'LocationID','null')
end
print #SQL
exec ( #SQL)