Does EXECUTE AS protect against SQL Injection? - tsql

I have a situation where I need to do this
CREATE PROCEDURE search_sp #condition varchar(8000) AS
SELECT * FROM tbl WHERE #condition
If I add a user to the database that only has the 'db_datareader' role and then use execute as to switch context to that user for the purposes of running the select statement, would this then protect me from SQL injection?
e.g.
DECLARE #cookie varbinary(100);
EXECUTE AS USER = 'restricted__user' WITH COOKIE INTO #cookie;
DECLARE #SQL AS NVARCHAR(MAX)
SET #SQL= 'SELECT * FROM tbl WHERE ' + #condition
EXEC sp_executesql #SQL
REVERT WITH COOKIE = #cookie;

No, it won't. This will limit the queries that could be run if injected if the "EXECUTE AS USER" is even evaluated. It won't stop something like foo' or 1 = 1; /*... which can be a case where "EXECUTE AS USER" is never reached. Properly handling the input is required.

What happens when I pass "1=1" as condition?
Now you send back all rows in 'tbl'. If table is, say, users, I now have all user names and all passwords (hopefully they're hashed and salted...).

Related

GRANT statements with bound parameters

I'm using a client library that only accepts SQL strings that are compile-time constant, in order to prevent SQL injection attacks. And I wanted to execute some GRANT statements for a set of tables and a user.
I tried
GRANT SELECT ON $1 TO $2
and passing the table and user names as bound parameters. But that fails with
syntax error at or near "$1"
Not being able to pass in a tablename as a bound parameter is understandable (you can't use SELECT columns FROM $1 for instance), and with a bit of work, I can make the tablenames compile-time constants. But changing the command to
GRANT SELECT ON MyTable to $1
and passing just the username as a bound parameter also fails. Which is more of an issue: whereas the tablenames can be hard-coded with a bit of work, the username is only known at runtime.
Is there a way to pass the username as a bound parameter, or do I need to bypass my client library in order to GRANT permissions to a run-time-defined username?
CREATE OR REPLACE FUNCTION test_grant (_role text)
RETURNS void
AS $$
DECLARE
_sql text := '';
BEGIN
_sql := 'GRANT SELECT ON a to ' || quote_ident(_role) || ' GRANTED BY current_user ';
RAISE NOTICE '%', _sql;
EXECUTE _sql;
RAISE NOTICE '% granted table a to %', CURRENT_USER, _role;
END
$$
LANGUAGE plpgsql
STRICT.
You can also make the table as function input argument. quote_ident is used for identifiers quoting. In GRANT SELECT ON MyTable to $1 you hope to make sure $1 is a identifiers rather than some string. Because if $1 string then the whole command can be:
GRANT SELECT ON MyTable to public;
GRANT SELECT ON MyTable to role_a WITH GRANT OPTION;
So the above function can solve these problem.
The only statements that can use parameters are INSERT, UPDATE, DELETE and SELECT. GRANT cannot use parameters; you will have to build a statement dynamically.

Delete from table in all databases containing table

I want to create a T-SQL query which are deleting all rows in the table Logins in ALL databases containing this exact table, so it can be run without any errors.
I want to reuse the code to other stuff as well, e.g. finding all active users from all databases containing the table Users. Therefore, I think, the best solution would be a pure T-SQL solution. This way the query can even become an automated job run by SQL Server Agent
Is it possible? And how?
Build some dynamic SQL:
declare #sql varchar(max) = ''
select #sql = #sql +
'use [' + name + '] ' +
'if exists (select * from sys.tables where name = ''Logins'') ' +
'delete from Logins '
from sys.databases where name not in ('master','model','msdb','tempdb')
print #sql
--exec (#sql)
Uncomment the exec line to actually run the code rather than just see what would be executed.

mysqli cannot call stored procedure

Procedure
delimiter $$
drop procedure if exists db1.test;
create procedure db1.test()
deterministic
begin
select * from table1;
end$$
delimiter ;
php code:
$conn = new mysqli('localhost','username','passwd','db1');
$query1 = 'select * from table1';
$query2 = 'call test()';
Then $conn->query($query1) works while $conn->query($query2) returns bool(false).
But in mysql, both query1 and query2 work.
What did I miss here? Thanks!
Okay, if it's not the syntax, it could be permissions. Did you grant execute privileges to the user for this database?
Edit:
Here's the SQL to do this:
GRANT EXECUTE ON `db1` . * TO 'user'#'localhost';
(Even if a user has all the permissions required to do the SQL inside the stored procedure as individual queries, you'd still need the EXECUTE privilege to actually call the procedure.)
Are you sure the procedure is being created? Both MySQL Workbench and phpMyAdmin are telling me there's a syntax error in select * from table; --- probably because table is a reserved word? This worked in MySQL Workbench:
USE `db1`;
DROP procedure IF EXISTS `test`;
DELIMITER $$
USE `db1`$$
CREATE PROCEDURE `db1`.`test` ()
BEGIN
select * from `table`;
END
$$
DELIMITER ;
Note the addition of backticks to table. With that change on my system, $query2 succeeds but $query1 fails (with or without the change to the procedure of course)

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)