Can I protect against SQL injection attacks by wrapping SQL in PostgreSQL functions? - postgresql

Can I use a function like this
CREATE FUNCTION create_user(_firstName text)
RETURNS void AS $$
INSERT INTO user_account (first_name) VALUES (_firstName);
$$ LANGUAGE sql;
On the server to protect against SQL injection attacks? Then I can just run this on the client,
client.query(`SELECT create_user(${someUserInput})...`
Or will I still need to use parameterized queries with placeholders,
client.query(`SELECT create_user($1)`, [someUserInput])

Problem (exploit)
client.query(`select create_user(${someUserInput})`
The problem there is what happens if
let someUserInput = `'foo'); DROP DATABASE bar;`;
That will get sent to your call as,
client.query("select create_user('foo'); DROP DATABASE bar;")`
And, that would be bad. Yes, the argument to create_user is protected against injection, but the call to that isn't.
Solutions
Use placeholders (obvious choice: most failsafe and secure solution.)
Ensure someUserInput is properly quoted
Use the client-library to quote it with something like PQescapeLiteral
Use a second run to the server to quote it with quote_literal (requires placeholders anyway). SELECT quote_literal($1);
I would not try to create the quoting-mechanism myself.

Related

Loading Postgres functions with dependencies

I'm using quite a few Postgres functions (both sql and pl/pgsql) in a particular application. Some of the sql functions depend on other sql functions, e.g.
create or replace function my_function ()
returns table (a text, b text) as
$$
select * from my_other_function();
$$
language sql;
For my_function to load properly, my_other_function has to be loaded first, else I get a my_other_function does not exist error. To manage this, I have been manually ensuring that my_other_function does get loaded first, but it would be nice not to have to do that.
In other words, is there a way to load all of my functions without regard to order and somehow check that all the necessary dependencies are available (function objects) after the fact?
I'm using Postgres 9.6.
You can use SETcheck_function_bodies= false; prior creating your functions to suppress the error.

Issue while storing json value with single quotes

I am facing issue while storing jason value which has some text with single quotes.
Below is the scenario,
My Stored Procedure:
CREATE OR REPLACE FUNCTION JsonParse(inputdata json)
RETURNS void AS $$
DECLARE
BEGIN
UPDATE
MyTable
SET
settings_details= inputdata
WHERE
settings_key='my-list';
END;
$$
LANGUAGE PLPGSQL;
select * from JsonParse('[{
"myArray": ["New Text1 ''abcd''", "New Text1 ''abcd''"]
},
{"myArray": ["New Text1 ''abcd''", "New Text1 ''abcd''"]}]');
I get the below error :
ERROR: syntax error at or near "abcd"
I can add extra single quote to abcd and efgh. It solves the issue. But the problem is that i dont have control on the input text to JsonParse procedure.
The stored procedure should be capable enough to handle this.
May i please know on how to tackle this
Your stored procedure is not rejecting this, the SQL you are running is malformed.
Try a simpler example:
SELECT 'test o'reilly' AS FOO;
As the syntax highlighting shows, the SQL parser will think the string is 'test o', and the reilly is a syntax error.
This is a sign that you are not correctly handling data input into your SQL queries, and are probably vulnerable to SQL injection attacks (what if I supply you with the "JSON" string {}'); DROP TABLE users; --).
Everywhere that you add data to an SQL query, you need to do one of two things:
Escape the input using an appropriate function provided by your language or framework. This will mostly just be doubling apostrophes, but if implemented properly will ensure there aren't other characters that can break the input based on the database's character set etc.
Or, use parametrised prepared queries, where you write the SQL with placeholders, and then provide the data as a separate argument, which is transmitted separately, so cannot be mistaken for SQL (and, consequently, doesn't need quotes around strings). In your case, a parametrised query might look like this: select * from JsonParse(?); with the execute function taking one parameter.

How is this code prone to SQL injection?

I was reading PostgreSql documentation here, and came across the following code snippet:
EXECUTE 'SELECT count(*) FROM mytable WHERE inserted_by = $1 AND inserted <= $2'
INTO c
USING checked_user, checked_date;
The documentation states that "This method is often preferable to inserting data values into the command string as text: it avoids run-time overhead of converting the values to text and back, and it is much less prone to SQL-injection attacks since there is no need for quoting or escaping".
Can you show me how this code is prone to SQL injection at all?
Edit: in all other RDBMS I have worked with this would completely prevent SQL injection. What is implemented differently in PostgreSql?
Quick answer is that it isn't by itself prone to SQL injection, As I understand your question you are asking why we don't just say so. So since you are looking for scenarios where this might lead to SQL injection, consider that mytable might be a view, and so could have additional functions behind it. Those functions might be vulnerable to SQL injection.
So you can't look at a query and conclude that it is definitely not susceptible to SQL injection. The best you can do is indicate that at the level provided, this specific level of your application does not raise sql injection concerns here.
Here is an example of a case where sql injection might very well happen.
CREATE OR REPLACE FUNCTION ban_user() returns trigger
language plpgsql security definer as
$$
begin
insert into banned_users (username) values (new.username);
execute 'alter user ' || new.username || ' WITH VALID UNTIL ''YESTERDAY''';
return new;
end;
Note that utility functions cannot be parameterized as you indicate, and we forgot to quote_ident() around new.username, thus making the field vulnerable.
CREATE OR REPLACE VIEW banned_users_today AS
SELECT username FROM banned_users where banned_date = 'today';
CREATE TRIGGER i_banned_users_today INSTEAD OF INSERT ON banned_users_today
FOR EACH ROW EXECUTE PROCEDURE ban_user();
EXECUTE 'insert into banned_users_today (username) values ($1)'
USING 'postgres with password ''boo''; drop function ban_user() cascade; --';
So no it doesn't completely solve the problem even if used everywhere it can be used. And proper use of quote_literal() and quote_ident() don't always solve the problem either.
The thing is that the problem can always be at a lower level than the query you are executing.
The bound parameters prevent garbage to manipulate the statement into doing anything other than what it's intended to do.
This guarantees no possibility for SQL-injection attacks short of a Postgres bug. (See H2C03's link for examples of what could go wrong.)
I imagine the "much less prone to SQL-injection attacks" amounts to CYA verbiage were such a thing were to arise.
SQL injection is usually associated with large data dumps on pastebin.com and such scenario won't work here even if the example used contatenation not variables. It's because that COUNT(*) will aggregate all data you'd be trying to steal.
But I can imagine scenarios where count of arbitrary record would be sufficiently valuable information - e.g. number of competitor's clients, number of sold products etc. And actually recalling some of the really tricky blind SQL injection methods it might be possible to build a query that using COUNT alone would allow to iteratively recover actual text from the database.
It would be also much easier to exploit on a database sufficiently old and misconfigured to allow the ; separator, in which case the attacker might just append a completely separate query.

Calling functions with exec instead of select

Is the default way of calling a function select * from my_function()?
I ask because I have built a function that doesn't return anything, just inserts data into a table and (coming from a SQL Server background) it "feels" strange to call it with select * from...
I was expecting something like exec my_function()
use PERFORM statement - http://www.postgresql.org/docs/current/static/plpgsql-statements.html
Sometimes it is useful to evaluate an expression or SELECT query but
discard the result, for example when calling a function that has
side-effects but no useful result value. To do this in PL/pgSQL, use
the PERFORM statement
so it's just
DO $$ BEGIN
PERFORM my_function();
END $$;
PostgreSQL 11:
PostgreSQL 11 supports true stored procedures as pointed out by #AbdisamadKhalif . They support in-procedure transaction control.
Older versions:
Yes, that's the standard way, and yes it's weird.
Usually you'd write such functions as stored procedures and invoke them with the CALL or EXECUTE command. PostgreSQL does not support true stored procedures (multiple result sets, autonomous transactions, and all that) though, only sql-callable user-defined functions.
So the workaround is to SELECT function_name() using the PostgreSQL extension syntax that omits FROM, or SELECT 1 FROM function_name(); to be (somewhat) more standard.
The ODBC driver, JDBC driver, etc understand the {call func_name()} escape syntax and automatically translate it to an underlying SELECT.
You will use from when the function returns a set. If the function returns void just do
select my_function();

What is the purpose of pgScript in PostgreSQL?

I have failed to understand the need for pgScript, which could be executed using pgAdmin tool. When it should be used? What it can do that plpgSQL cannot do? What is equivalent of it in Microsoft SQL Server?
pgScript is a client-side scripting language, while pl/PgSQL runs on the server. This means they have entirely different use cases. For example, PgScript can manage transaction status while pl/PgSQL cannot, but pl/Pgsql can be used to extend the language of SQL while pgScript cannot do that.
Additionally it means the two will handle many other things quite differently ranging from query plans to dynamic SQL, and while pgScript requires round trips between queries pl/Pgsql does not.
One use for pgScript is to define variables and use them later in your SQLs.
For example, you could do something like this:
declare #mytbl, #maxid;
set #mytbl = 'sometable';
set #maxid = 2500;
set #res = select count(*) from #mytbl where id <= #maxid;
print #res;
This approach is to just have any variables you want to change at the top of your script, rather than those getting buried deep inside complex SQL queries.
Of course, pgScript is a feature available only inside PgAdmin III client like #{Craig Ringer} mentioned in his comment.
I used DDL script generator from Toad Data Modeler.
I ran the script using normal query execution in PgAdmin-III but it kept giving me error:
"ERROR: relation "user" already exists SQL state: 42P07".
I ran Execute pgScript in PgAdmin-III and it worked fine.