There are already some very good texts about it on web and I do not want to “steal” credit from them:
There are just some of my experiencies:
- If you can use prepared statements instead of dynamic queries
- If you must use dynamic queries (which can be more often case) try to follow these rules:
- If you can, always use list of values not allowing manually typed value – even for numbers. In many cases user can choose from some arbitrary set of distinct values like for age, year etc. and it will prevent mistakes and unrealistic values.
- If you must allow “raw” input value then
- If it is numeric, always validate it – simplest validation is cast from string to number
- If it is string then wrap it using “quote_literal” or “quote_nullable” functions
You can test on this simple code. Of course do not try anything destructive 🙂
do $$ declare _t text; _q text; _r text; _n numeric; _des text; _querymask text; begin -- query _querymask := 'select tableowner from pg_tables where tablename='; -- injected text _t := 'NULL; select current_database();'; _des := 'query without quote function'; _q := _querymask||_t; raise notice '%: %', _des, _q; execute _q into _r; raise notice 'returned value: %', _r; _des := 'query with quote function'; _q := _querymask||quote_nullable(_t); raise notice '%: %', _des, _q; execute _q into _r; raise notice 'returned value: %', _r; -- query _querymask := 'select relname from pg_class where oid='; --test with numeric value _t := '0; select current_database();'; _des := 'query without testing input for numeric validation'; _q := _querymask||_t; raise notice '%: %', _des, _q; execute _q into _r; raise notice 'returned value: %', _r; _des := 'query with testing input for numeric validation'; begin _q := _querymask||cast(_t as numeric); raise notice '%: %', _des, _q; execute _q into _r; raise notice 'returned value: %', _r; exception when invalid_text_representation then raise notice 'invalid input for numeric value'; end; end; $$ language plpgsql;