Thread: seeking advices for function
Hi list, I've got some questions about this function: ============================= DROP FUNCTION tsttst(TEXT, TEXT, BOOLEAN, INT8, INT8, TEXT[]); CREATE FUNCTION tsttst(TEXT, -- FQTN TEXT, -- Ordering column BOOLEAN, -- TRUE=ASC / FALSE=DESC INT8, -- LIMIT INT8, -- OFFSET TEXT[]) -- Columns' names array RETURNS SETOF RECORD AS $$ DECLARE usr TEXT; ord TEXT; collist TEXT; qry TEXT; BEGIN -- Retrieve real caller's name usr := session_user; -- First check for table SELECT privilege IF NOT has_table_privilege(usr, $1, 'SELECT') THEN -- If needed, check SELECT privilege per column FOR i IN 1 .. array_length($6, 1) LOOP IF NOT has_column_privilege(usr, $1, $6[i], 'SELECT') THEN RAISE EXCEPTION 'FNCT: tsttst: Call forbidden'; -- ADD HERE LOGING IN TABLE security.alert -- YEAH, BUT HOW TO AVOID security.alert NEW ROW BEING -- VOIDED FROM A ROLLBACK ??? END IF; END LOOP; END IF; -- Set ordering direction IF $3 THEN ord := 'ASC'; ELSE ord := 'DESC'; END IF; -- Construct columns full list collist := array_to_string($6, ','); -- Build query from input parms qry = 'SELECT ' || collist || ' FROM ' || $1 || ' ORDER BY ' || $2 || ' ' || ord || ' LIMIT ' || $4 || ' OFFSET ' || $5 || ';'; -- Return the whole query RETURN QUERY EXECUTE qry; END; $$ LANGUAGE plpgsql STABLE STRICT SECURITY DEFINER; ============================= * Is it totally non-vulnerable to SQL injection? * I intend to use this kind of function for data I/O, as a replacement for views in an ERP project. Considering that overhead introduced by builtin SELECT privilege check is far from negligible (from 110ns to 800ns, one row select) but on the other hand that it could replace thousands views and that an ERP isn't an application that generates hundreds queries per second. Is it a good idea or not? * A big problem is the implementation of trespassing attempts loging (see comment in function) which shouldn't be subject to the subsequent rollback; how can I do that? Any constructive critics will be welcome. JY -- My doctor told me to stop having intimate dinners for four. Unless there are three other people. -- Orson Welles
On Wed, Jun 22, 2011 at 2:30 PM, Jean-Yves F. Barbier <12ukwn@gmail.com> wrote: > Hi list, > > I've got some questions about this function: > > ============================= > DROP FUNCTION tsttst(TEXT, TEXT, BOOLEAN, INT8, INT8, TEXT[]); > CREATE FUNCTION tsttst(TEXT, -- FQTN > TEXT, -- Ordering column > BOOLEAN, -- TRUE=ASC / FALSE=DESC > INT8, -- LIMIT > INT8, -- OFFSET > TEXT[]) -- Columns' names array > RETURNS SETOF RECORD AS $$ > DECLARE > usr TEXT; > ord TEXT; > collist TEXT; > qry TEXT; > BEGIN > -- Retrieve real caller's name > usr := session_user; > -- First check for table SELECT privilege > IF NOT has_table_privilege(usr, $1, 'SELECT') THEN > -- If needed, check SELECT privilege per column > FOR i IN 1 .. array_length($6, 1) LOOP > IF NOT has_column_privilege(usr, $1, $6[i], 'SELECT') THEN > RAISE EXCEPTION 'FNCT: tsttst: Call forbidden'; > -- ADD HERE LOGING IN TABLE security.alert > -- YEAH, BUT HOW TO AVOID security.alert NEW ROW BEING > -- VOIDED FROM A ROLLBACK ??? > END IF; > END LOOP; > END IF; > -- Set ordering direction > IF $3 THEN > ord := 'ASC'; > ELSE > ord := 'DESC'; > END IF; > -- Construct columns full list > collist := array_to_string($6, ','); > -- Build query from input parms > qry = 'SELECT ' || collist || ' FROM ' || $1 || ' ORDER BY ' || $2 || ' ' > || ord || ' LIMIT ' || $4 || ' OFFSET ' || $5 || ';'; > -- Return the whole query > RETURN QUERY EXECUTE qry; > END; > $$ LANGUAGE plpgsql STABLE STRICT SECURITY DEFINER; > ============================= > > * Is it totally non-vulnerable to SQL injection? > > * I intend to use this kind of function for data I/O, as a replacement for > views in an ERP project. > Considering that overhead introduced by builtin SELECT privilege check is > far from negligible (from 110ns to 800ns, one row select) but on the other > hand that it could replace thousands views and that an ERP isn't an > application that generates hundreds queries per second. > Is it a good idea or not? > > * A big problem is the implementation of trespassing attempts loging (see > comment in function) which shouldn't be subject to the subsequent rollback; > how can I do that? > > Any constructive critics will be welcome. I think it's much better to use the database log to record security violations. Wrapping SQL with a function like this is going to be a problem factory. For example, it's a total optimization fence if you ever need to do something like join against your 'view'. IMO, it's a total non-starter. If you *must* log to a table in a view definition, or want to sneakily hook custom behaviors to a view generally, you can do something like this. say your view is: CREATE VIEW v as SELECT * FROM foo; organize your plpgsql function like this: CREATE FUNCTION priv_check(_view text) RETURNS bool AS $$ BEGIN IF NOT has_table_privilege(session_user, _view, 'SELECT') THEN /* insert into log via dblink (see dblink docs) */ RAISE ... END IF; RETURN true; END; $$ LANGUAGE PLPGSQL; now you can work up your view like this: CREATE VIEW v as SELECT * FROM foo WHERE (SELECT priv_check('foo')); I'm pretty sure postgres is going to be smart enough to run priv_check only once per select from the view in all reasonable cases. dblink remains the only way to emit records you want to keep from a transaction that you want to roll back without recovering. merlin
On Wed, 22 Jun 2011 15:07:16 -0500, Merlin Moncure <mmoncure@gmail.com> wrote: ... > > I think it's much better to use the database log to record security > violations. Ok, so I suppose I have to use such a program as pg_fouine (or even just a script with greps) and email results to the DBA. > Wrapping SQL with a function like this is going to be a > problem factory. For example, it's a total optimization fence if you > ever need to do something like join against your 'view'. IMO, it's a > total non-starter. This function is to be used against only one table; for joined queries, I intend to use the same kind of function, however involving all needed tables. The goal is (if possible) eliminate views because if I've got 200 user profiles, I'll be obliged to generate 200 x (many)viewS. > If you *must* log to a table in a view definition, or want to sneakily > hook custom behaviors to a view generally, you can do something like > this. > > say your view is: > CREATE VIEW v as SELECT * FROM foo; > > organize your plpgsql function like this: > CREATE FUNCTION priv_check(_view text) RETURNS bool AS > $$ > BEGIN > IF NOT has_table_privilege(session_user, _view, 'SELECT') THEN > /* insert into log via dblink (see dblink docs) */ > RAISE ... > END IF; > > RETURN true; > END; > $$ LANGUAGE PLPGSQL; > > now you can work up your view like this: > CREATE VIEW v as SELECT * FROM foo WHERE (SELECT priv_check('foo')); > > I'm pretty sure postgres is going to be smart enough to run priv_check > only once per select from the view in all reasonable cases. dblink > remains the only way to emit records you want to keep from a > transaction that you want to roll back without recovering. Ok, I keep that idea in mind :), thanks. > merlin -- X-rated movies are all alike ... the only thing they leave to the imagination is the plot.
On Wed, Jun 22, 2011 at 3:45 PM, Jean-Yves F. Barbier <12ukwn@gmail.com> wrote: > On Wed, 22 Jun 2011 15:07:16 -0500, Merlin Moncure <mmoncure@gmail.com> wrote: > > ... >> >> I think it's much better to use the database log to record security >> violations. > > Ok, so I suppose I have to use such a program as pg_fouine (or even just a > script with greps) and email results to the DBA. > >> Wrapping SQL with a function like this is going to be a >> problem factory. For example, it's a total optimization fence if you >> ever need to do something like join against your 'view'. IMO, it's a >> total non-starter. > > This function is to be used against only one table; for joined queries, > I intend to use the same kind of function, however involving all needed tables. > > The goal is (if possible) eliminate views because if I've got 200 user > profiles, I'll be obliged to generate 200 x (many)viewS. why in the world do you need to create one view/user/table? that is absolutely something you would want to avoid... merlin
On Wed, 22 Jun 2011 16:27:39 -0500, Merlin Moncure <mmoncure@gmail.com> wrote: ... > > why in the world do you need to create one view/user/table? that is > absolutely something you would want to avoid... No, I didn't meant one view/user/table; what I meant is, for example, that for some tables (that just need to be read alone, w/o joins) I'll juste have one function instead of Nb tables x view - furthermore, I can't use a view for some tables as I need to slice answers eg: suppose I've got 15,000 clients, I can't load the whole list at once; and AFAIK views can't do that. I also meant using this kind of function (extended of course) to retrieve joined rows. But even if I don't have a view per table, my application needs a bunch of them, which needs to be multiplied by the number of users profiles (eg: buyers can set purchase price & minimum profit margin up, but salers won't; so, for this example we already have 2 different views, which also means we must have 2 different schemas for these categories of users... and so on) Thing would be *much* easier if a 'SELECT *' returned only the columns on which user have the SELECT privilege - I guess SQL standard forbid that &| it is hard to implement. -- Necessity has no law. -- St. Augustine