2

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?

A-K
  • 16,804
  • 8
  • 54
  • 74
  • 1
    http://security.stackexchange.com/questions/15214/are-prepared-statements-100-safe-against-sql-injection –  Nov 01 '13 at 13:12
  • @H2CO3 that's a useful link, but I would not call that SQL Injection. – A-K Nov 01 '13 at 13:20
  • 2
    Call *what* SQL injection? The accepted answer there says that "no, SQL injection is not possible". –  Nov 01 '13 at 13:27
  • @H2CO3 exactly. However, that question is not specific to PostgreSql at all, so I am not sure it fully answers my question. – A-K Nov 01 '13 at 13:43
  • Why wouldn't it? Doesn't PostgreSQL have prepared statements? Doesn't it handle them just like practically any other SQL/RMDB engine does? –  Nov 01 '13 at 13:44
  • @H2CO3 of course not. Every RDBMS is implemented at least slightly differently. For example, PostgreSql "will re-plan the command on each execution, generating a plan that is specific to the current parameter values". Sql Server may reuse an existing plan. – A-K Nov 01 '13 at 13:54
  • Then you're looking for implementation details of PostgreSQL. –  Nov 01 '13 at 13:55
  • @H2CO3 Of course - in all other RDBMS I have worked with this would completely prevent SQL injection. – A-K Nov 01 '13 at 13:57

3 Answers3

2

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.

Chris Travers
  • 25,424
  • 6
  • 65
  • 182
1

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.

Community
  • 1
  • 1
Denis de Bernardy
  • 75,850
  • 13
  • 131
  • 154
0

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.

kravietz
  • 10,667
  • 2
  • 35
  • 27
  • 2
    *SQL injection is usually associated with large data dumps on pastebin.com*. What? – Tom Anderson Nov 04 '13 at 10:57
  • Read: when you say "SQL injection" people usually think about massive data breach, like whole table dumped by sqlmap. But you can steal data even through COUNT on arbitrary columns. – kravietz Nov 04 '13 at 14:24