Understanding PostgreSQL & plpgsql defense against code injection - sql

On this very useful page there is this statement...
CREATE OR REPLACE FUNCTION user_access (p_uname TEXT)
RETURNS timestamp LANGUAGE sql AS
$func$
SELECT accessed_at FROM users WHERE username = $1
$func$;
...as an explanation of how to be safe from SQL injection. I am trying to understand why i would be safe this way. What does PostgreSQL do for me, that keeps me safe? Can someone please point me in the right direction?

Why is it safe?
PostgreSQL (and about any other DBMS) will first compile the statement with the $1 in place, and only then will use the actual value provided by the user. So whatever is in the value cannot be interpreted as a part of the statement. In other words, using the parameter separates code from data.
Why do I need a procedure for this?
In most cases, you don't. What you really need is a mechanism that separates code from data, i.e. the parameter. In most languages and DB connectivity libraries nowadays there is a parameterized query support present, which allows you to write queries with placeholders (such as ?) and provide data through a separate parameter.

Related

Prevent SQL Injection in Go Postgresql

I research around the forum of postgresql injection in Go and I found some useful information in SQL injection like below:
How to execute an IN lookup in SQL using Golang?
How can I prevent SQL injection attacks in Go while using "database/sql"?
but I still need some advice because my code in Go is using a different kind of code and usecases.
some usecase/question i need advice for are like this
Using query looping to multiple insert like
INSERT INTO a (a1,a2,a3) VALUES (%d,%d,%s) using fmt.Sprintf, I know using sprinft is bad. so is there any solution for this loop query for insert ?
Ex: INSERT INTO a (a1,a2,a3) VALUES (%d,%d,%s),(%d,%d,%s),(%d,%d,%s)
Is it safe to use fmt.Sprintf to generate query if the param is using %d instead of %s ?
Using Prepare statement and Query is safe, but what if I'm using function Select (using $1,$2) and function NamedQuery (using struct named.)
Ex: Select * from a where text = $1 -> is using this $1 safe ?
and
Ex : Select * from a where text = :text -> is this safe in function NamedQuery?
Kindly need your advice guys. Thank you!
Firstly, usually prefer to use the db placeholders ? $1 etc.
Yes it is safe to use fmt.Sprintf with integer parameters to build SQL, though worth avoiding if you can, but your third param is %s - avoid that and use ?
Yes it is safe to use fmt.Sprintf with integer parameters, but %s or %v is far more risky and I'd avoid, can't think why you'd need it.
Use placeholders here, then yes it is safe.
General rules:
Use placeholders by default, it should be rare to use %d (as in your IN query for example)
Parse params into types like integer before any validation or use
Avoid string concat if you can, and be particularly wary of string params
Always hard code things like column and table names, never generate them from user input (e.g. ?sort=mystringcolname)
Always validate that the params you get are only those authorised for that user

What does the code following CREATE FUNCTION being a string imply?

From a great reply:
in PostgreSQL, CREATE FUNCTION is indeed a "SQL statement" but is is merely a
"wrapper" to specify a block of code that is executed by something
different than the SQL query "engine". Postgres (unlike other DBMS)
supports multiple "runtime engines" that can execute the block of code
that was passed to the "CREATE FUNCTION" statement - one artifact of
that is that the code is actually a string so CREATE FUNCTION only
sees a string, nothing else.
What are the consequences of "the code is actually a string so CREATE FUNCTION only sees a string, nothing else"?
Is that considered as dynamic SQL? Does it prevent or introduce SQL injection risk, compared to dynamic SQL?
How is that different from other RDBMS (if any?) where "the code is not a string"?
Thanks.
PostgreSQL is highly extensible, and you can for example define your own procedural language to write functions in.
PostgreSQL knows nothing about the language except that it has to call a certain language handler to execute the function.
The way that was chosen to implement this is to simplify pass the code as a string.
This is just an implementation detail and does not make PostgreSQL functions any more or less vulnerable to SQL injection than other RDBMS.
There are several levels on which you have to defend yourself against injection:
The function arguments: Here you should choose non-string data types whenever possible.
The SQL statements within the function: Here you should avoid dynamic SQL whenever possible, and if you have to use dynamic SQL, you should insert variables using the %L pattern of the format function.
Again, this is the same if function bodies are specified as strings or not.
All 3GL+ code is basically a string. The "parameter" passed to CREATE FUNCTION is code (to be executed outide the core SQL engine), which is a string (that's not SQL).
Other RDMS's only support SQL as the function/procedure body.

How PDO prepared statements help to prevent SQL vulnerable statements?

I'm so confused or rather I'm like, soooooooooo confused with pdo prepared statements. I know that prepared statements are the best way to keep data safe from hackers.
From : How can prepared statements protect from SQL injection attacks?
We are sending program to the server first
$db->prepare("SELECT * FROM users where id=?"); where the data is
substituted by some variable called "placeholder".
Note that the very same query being sent to the server, without any
data in it! And then we're sending the data with the second request,
totally separated from the query itself:
$db->execute($data);
query-
$query=$db->prepare("SELECT * FROM USERS WHERE username=?");
$query->execute(array($tex));
$tex=blah; DROP TABLE users;--
then it will be like - SELECT * FROM USERS WHERE username=blah; DROP TABLE users;--
how prepare statements will help me with this example above?
I'm really sorry if this question is vague to understand. Any help would be appreciated. Thanks in advance.
The prepared statement handler will make sure the bound value is always used as valid SQL value/literal (ie. an SQL string or a number) and never as 'raw SQL text'1.
This is why placeholders values cannot be used as identifiers such as column or table names or act as other SQL keywords; and cannot generate the vulnerable query hypothesized. Instead it is treated as the following:
WHERE username='blah; DROP TABLE users;--'
--^ placeholder ensures valid SQL string value is used
-- (note automatic/implicit addition of SQL quotes)
And even when binding with 'more tricky' data:
$tex = "blah'; DROP TABLE users;--"; // embedded SQL quote character
It would still be safe:
WHERE username='blah''; DROP TABLE users;--'
--^ placeholder STILL ensures valid SQL string value is used
Thus, when using placeholders, it is impossible to generate the SQL that is vulnerable (in this way).
For SQL Injection the 'shape' of the query (which includes keywords and identifiers, but excludes values) must itself be altered by the input.
1 Technically placeholders values can also be sent through a separate data channel (depending on adapter/driver) and thus might not even appear in the raw SQL query itself.
However a simple way to think about why placeholders are safe, or how they 'work' is:
When using placeholders the adapter ensures that the equivalent of 'sql really safe escape' and applicable quoting is always used for every bound text value - and is thus impossible for accidentally forget.

PDO prepare statements: Do we need to escape?

public function receiveDomainNames($keyword)
{
try
{
$stmt = $this->_dbh->prepare("SELECT d.someField FROM domain d WHERE d.someField LIKE :keyword");
$someField = '%'.$keyword.'%';
Do we need to escape $keyword on this case?
On php manual we can read:
If an application exclusively uses prepared statements, the developer can
be sure that no SQL injection will
occur (however, if other portions of
the query are being built up with
unescaped input, SQL injection is
still possible).
Is this the case on your opinion, are, on this case, build up unescaped input (no prior treatment has been made to our $keyword parameter) ?
Thanks in advance,
MEM
Given the above SQL statement, I see no rational possibility of a SQL injection.
What the warning about "other parts" would be a SQL query like:
$binds = array(":id"=>$_GET['id']);
$myPDO->prepare("SELECT {$_GET['columns']} FROM {$_GET{['table']} WHERE id = :id");
$statement = $myPDO->execute($binds);
The example is a worst case/explicit example of what they mean, that naively someone might think since they're escaping the where argument, that everything is safe.
With your example above, there is no un-escaped input so you're safe.
If an application exclusively uses prepared statements, the developer
can be sure that no SQL injection will occur (however, if other
portions of the query are being built up with unescaped input, SQL
injection is still possible).
I'd figure variables you create shouldn't have to be escaped because you know what they're doing.
Only escape content gotten from the user, such as $_COOKIE, $_POST, $_GET and other parameters such as the URL.

Parameterise table name in .NET/SQL?

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,