Dynamic SQL with parameters, when parameter value has predefined operators inside - tsql

I have a situation in a T-SQL stored procedure, where in a dynamic SQL, the parameter/s, referencing other variables, whose value has single quotes and other predefined operators. The problem is T-SQL script fails, when such a condition exist.
Attached is a sample code, demonstrating such a situation.
Any Idea how to solve such a case?
DECLARE #TransVocObj XML,#xmlfragment XML,#SQL NVARCHAR(MAX)
SELECT #TransVocObj = '<TransactionVoucherViewModel><TransactionRows></TransactionRows></TransactionVoucherViewModel>'
DECLARE #Narration varchar(100)
SET #Narration ='AABBCC''DD''EEFF'-- #Narration ='AABBCCDDEEFF'
Select #xmlfragment=
'<TransactionRow>'+'<Description>'+#Narration +'</Description>'+'<DebitAmount>'+CONVERT(VARCHAR(30),500.00)+'</DebitAmount>'+'</TransactionRow>'
SET #SQL=N' SET #TransVocObj.modify(''insert '+ CONVERT(NVARCHAR(MAX),#xmlfragment)+' into (/TransactionVoucherViewModel/TransactionRows)[1] '') '
EXECUTE sp_executesql #SQL,N'#TransVocObj XML Output,#xmlfragment XML',#TransVocObj OUTPUT,#xmlfragment
SELECT T.Item.query('.//Description').value('.','VARCHAR(60)') FROM #TransVocObj.nodes('//TransactionRows/TransactionRow') AS T(Item)
The database server is MS SQL SERVER 2005

You can double-up your single-quote characters within #Narration using the REPLACE function. So, when you build #xmlfragment it can look like:
Select #xmlfragment=
'<TransactionRow>'+'<Description>'+REPLACE(#Narration,'''','''''')+'</Description>'+'<DebitAmount>'+CONVERT(VARCHAR(30),500.00)+'</DebitAmount>'+'</TransactionRow>'

Related

Use function variable in dynamic COPY statement

According to docs of PostgreSQL it is possible to copy data to csv file right from a query without using an intermediate table. I am curious how to do that.
CREATE OR REPLACE FUNCTION m_tbl(my_var integer)
RETURNS void AS
$BODY$
DECLARE
BEGIN
COPY (
select my_var
)
TO 'c:/temp/out.csv';
END;
$$ LANGUAGE plpgsql;
I get an error: no such column 'my_var'.
Yes, it is possible to COPY from any query, whether or not it refers to a table.
However, COPY is a non-plannable statement, a utility statement. It doesn't support query parameters - and query parameters are how PL/PgSQL implements the insertion of variables into statements.
So you can't use PL/PgSQL variables with COPY.
You must instead use dynamic SQL with EXECUTE. See the Pl/PgSQL documentation for examples. There are lots of examples here on Stack Overflow and on https://dba.stackexchange.com/ too.
Something like:
EXECUTE format('
COPY (
select %L
)
TO ''c:/temp/out.csv'';
', my_var);
The same applies if you want the file path to be dynamic - you'd use:
EXECUTE format('
COPY (
select %L
)
TO %L;
', my_var, 'file_name.csv');
It also works for dynamic column names but you would use %I (for identifier, like "my_name") instead of %L for literal like 'my_value'. For details on %I and %L, see the documentation for format.

PostgreSQL Dollar-Quoted Strings Constants to Prevent SQL Injection

Can I safely prevent SQL Injection using PostgreSQL's Dollar-Quoted String Constants?
I know the best was to handle dynamic queries is to have them generated in a application layer with a parametrized query, that's not what this question is about. All of the business logic is in stored procedures.
I have a stored procedure that takes parameters and generates a query, runs it, formats the results and returns it as a chunk of text. This function is passed a table name, column names and WHERE parameters. The WHERE parameters passed to the function are from user entered data in the database. I would like to make sure that the stings are sanitized so the query that is built is safe.
Using PostgreSQLs Dollar-Quoted Strings Constants, I should be able to safely sanitize all string input other than ' $$ '. However, if I do a string replace on "$" to escape it, I should be able to do a string comparison that is safe.
Stored Procedure:
function_name(tablename text, colnames text[], whereparam text)
--Build dynamic query...
Function Call:
SELECT
function_name('tablename', ARRAY['col1', 'col2', 'col3'], 'AND replace(col1, ''$'', ''/$'') = $$' || replace(alt_string_col, '$', '/$') || '$$ ')
FROM alttable
WHERE alt_id = 123;
Query Generated:
SELECT col1, col2, col3 FROM tablename WHERE 1=1 AND replace(col1, '$', '/$') = $$un/safe'user /$/$ data;$$
Since I'm escaping the col1 field before I compare it to escaped user data, even if the user enters, "un/safe'user $$ data;" in the field, alt_string_col, the double dollar sign does not break the query and the comparison passes.
Is this a safe way to escape strings in PostgreSQL stored procedure?
Edit1
Thanks to Erwin Brandstetter. Using the USING clause for EXECUTE I was about to create a function that can be called like this:
SELECT function_name(
'tablename',
ARRAY['col1', 'col2', 'col3'],
ARRAY[' AND col1 = $1 ', ' OR col2 = $5 '],
quote_literal(alt_string_col)::text, --Text 1-4
NULL::text,
NULL::text,
NULL::text,
alt_active_col::boolean, --Bool 1-4
NULL::boolean,
NULL::boolean,
NULL::boolean,
NULL::integer, --Int 1-4
NULL::integer,
NULL::integer,
NULL::integer
)
FROM alttable
WHERE alt_id = 123;
It gives some flexibility to the WHERE clauses that can be passed in.
Inside the stored procedure I have something like this for the EXECUTE statement.
FOR results IN EXECUTE(builtquery) USING
textParm1,
textParm2,
textParm3,
textParm4,
boolParm1,
boolParm2,
boolParm3,
boolParm4,
intParm1,
intParm2,
intParm3,
intParm4
LOOP
-- Do some stuff
END LOOP;
Use quote_ident() to safeguard against SQL injection while concatenating identifiers. Or format() in Postgres 9.1 or later.
Use the USING clause for EXECUTE in PL/pgSQL code to pass values. Or at least quote_literal().
To make sure a table name exists (and is quoted and schema-qualified automatically where necessary when concatenated), use the special data type regclass.
More about executing dynamic SQL with PL/pgSQL:
PostgreSQL parameterized Order By / Limit in table function
Table name as a PostgreSQL function parameter
Since PostgreSQL 9.0 you can also use anonymous code blocks with the DO statement to execute dynamic SQL.

Dynamic conditions in WHERE clause

I have a stored procedure and would like to know if its possible to build up a dynamic where condition based on a parameter.
Lets say I have this query:
SELECT *
FROM tbl_Users
Now, I have a parameter called #username, which I would like to use to build up a dynamic where condition (which through my program might be 1 or more conditions). To achieve something like that I use the following statement:
SELECT *
FROM tbl_Users
#username -- where this parameter might hold a condition string such as "Where usr_Username = 5 and usr_first_name like '%Frank%' etc
Is it possible to do something like this?
You're going to have to break into dynamic sql for this.
it would run something like this:
declare #sql varchar(max)
set #sql = '
SELECT *
FROM tbl_Users
WHERE ' + #username
exec (#sql)
I'm not certain I understand you, but if my understanding is correct, you can do the following (NOTICE: injection vulnerable)
DECLARE #SQL varchar(500) = 'SELECT * FROM tbl_users ' + #username
EXEC #SQL
From what I know, this is not going to work. You're going to need to generate the script you want to execute and use the exec command.
You are really not supposed to be concatenating SQL keywords and parameters into one single string as shown in some of the responses above for reasons of opening the doors to SQL injection (One of the contributors actually called it out. That's a wise Warning!).
Instead, you are supposed to parameterize your SQL and execute the system SP sp_executesql.
A very good code example is shown in this StackOverflow posting.

T-SQL Table name alias

In my T-SQL script, I refer to same long table name several times. I use this query on different tables.
Is there a way to refer a table name by variable? If so, I can simple declare one variable at the top which script will use and just by setting value, I can run it on various tables without making changes in the script.
A couple of options.
Within a single SQL statement you can alias table names like so:
SELECT *
FROM MySuperLongTableName T
WHERE T.SomeField=1
If you need to do this over lots of statements across several scripts a synonym might be a better option:
CREATE SYNONYM SuperT FOR dbo.MySuperLongTableName
You could create a synonym for that table but obviously you'd need to make sure that nobody changed the definition of the synonym whilst the script was running (and no parallel invocations of the script)
Are you running these in SSMS? If so you could set SQL CMD mode (on the "Query" menu) and use
:setvar tablename "spt_values"
use master
select * from $(tablename)
You could do this as such:
Declare #TableName As nvarchar(max)
Declare #SQL AS nvarchar(max)
#TableName = 'longtablename'
#SQL = 'Select * From ' + #TableName
EXEC(#SQL)

Parameters in the FormsOf function and SQL injection

Is the following SQL susceptible to SQL injection via the #SearchWord parameter?
I want to use parameters with the FormsOf function, but the only guide to doing so I've found is in this Stack Overflow question: How to pass parameter to FormsOf function in sql server
However the solution seems to be to use a bit of dynamic SQL, and I was wondering if that would be susceptible to SQL injection. What would happen in the following example if #searchWord contained a SQL injection type string? Is it not a problem because it's still within a parameter, passed as an argument to FREETEXTTABLE?
The solution given is:
DECLARE #SearchWord nvarchar(max)
SET #SearchWord = 'tax'
DECLARE #SearchString nvarchar(max)
SET #SearchString = 'FormsOf(INFLECTIONAL, "' + #SearchWord + '")'
SELECT listing_id, RANK, name, address, city, zip, heading, phone
FROM listings a,
FREETEXTTABLE(listings, *, #SearchString)
WHERE [KEY] = a.listing_id
ORDER BY RANK DESC, name
No, it's not susceptible. There's no dynamic SQL here (that would require either using EXEC or sp_executesql), so there's no vector for SQL injection.
In order for a SQL injection vulnerability to exist, the user-supplied string (in this case #SearchWord) must actually be inserted directly into the text of the SQL statement. Here, it's only being used to construct another string variable, which is subsequently used as a parameter to another SQL statement.
This statement can, however, fail if the user inputs an "invalid" search word, i.e. one containing single quotes, so you should probably still escape whatever value is passed to #SearchWord. But it cannot be used to execute arbitrary SQL.
I haven't tested this, but I don't think the interpreter is simply pasting the value of #SearchString into the statement. It should parse #SearchString using the rules that FREETEXTTABLE expects--that's the way other parameters work.