OnlyPHP

PostgreSQL quote ident and literal

PostgreSQL quote ident and literal  

2013-05-21 16:08:58|  分类: PgSQL Develop |  标签: |字号 订阅

 
 
前面一篇介绍了SQL注入, 其中利用字符逃逸漏洞的占了主要部分.
 
PostgreSQL 提供了几个函数用来输出转义后的字符串.
quote_ident(string text) text Return the given string suitably quoted to be used as an identifier in an SQL statement string. Quotes are added only if necessary (i.e., if the string contains non-identifier characters or would be case-folded). Embedded quotes are properly doubled. See also Example 40-1. quote_ident('Foo bar') "Foo bar"
quote_literal(string text) text Return the given string suitably quoted to be used as a string literal in an SQL statement string. Embedded single-quotes and backslashes are properly doubled. Note that quote_literal returns null on null input; if the argument might be null, quote_nullable is often more suitable. See also Example 40-1. quote_literal(E'O\'Reilly') 'O''Reilly'
quote_literal(valueanyelement) text Coerce the given value to text and then quote it as a literal. Embedded single-quotes and backslashes are properly doubled. quote_literal(42.5) '42.5'
quote_nullable(string text) text Return the given string suitably quoted to be used as a string literal in an SQL statement string; or, if the argument is null, return NULL. Embedded single-quotes and backslashes are properly doubled. See also Example 40-1. quote_nullable(NULL) NULL
quote_nullable(valueanyelement) text Coerce the given value to text and then quote it as a literal; or, if the argument is null, return NULL. Embedded single-quotes and backslashes are properly doubled. quote_nullable(42.5) '42.5'
一般可用于构造SQL语句, 例如dblink中的动态SQL语句的构造 : 
防止因为输入问题造成的不正确SQL.
quote_ident用于构造数据库对象名, 例如表名, 列名.
quote_literal用于构造字符串.
quote_nullable和quote_literal类似, 只是空参数的处理不一样.
quote_literal是strict的, 输入空, 返回空.
quote_nullable是not strict的, 内部处理空值, 返回NULL字符串.

 

digoal=# select proisstrict,provolatile,proname from pg_proc where proname ~ 'quote';
 proisstrict | provolatile |    proname     
-------------+-------------+----------------
 t           | i           | quote_ident
 t           | i           | quote_literal
 t           | s           | quote_literal
 f           | i           | quote_nullable
 f           | s           | quote_nullable
(5 rows)
src/backend/utils/adt/quote.c

 

/*
 * quote_nullable -
 *        Returns a properly quoted literal, with null values returned
 *        as the text string 'NULL'.
 */
Datum
quote_nullable(PG_FUNCTION_ARGS)
{
        if(PG_ARGISNULL(0))
                PG_RETURN_TEXT_P(cstring_to_text("NULL"));
        else
                PG_RETURN_DATUM(DirectFunctionCall1(quote_literal,
                                                                                        PG_GETARG_DATUM(0)));
}
quote_nullable和quote_literal的差异 : 

 

digoal=# select 1 where quote_nullable(null) is null;
 ?column? 
----------
(0 rows)
 
digoal=# select 1 where quote_literal(null) is null;
 ?column? 
----------
        1
(1 row)
 
digoal=# select 1 where quote_literal(null)='NULL';
 ?column? 
----------
(0 rows)
 
digoal=# select 1 where quote_nullable(null)='NULL';
 ?column? 
----------
        1
(1 row)
 
 
构造动态SQL举例.
不使用quote : 

 

digoal=# create or replace function f_test(i_tablename text, i_cname text, i_cval text) returns void as $$
declare 
  v_sql text;
begin
  v_sql := 'create table '||i_tablename||'('||i_cname||' text)';
  raise notice '%', v_sql;
  execute v_sql;
  v_sql := 'insert into '||i_tablename||'('||i_cname||') values ('||i_cval||')';
  raise notice '%', v_sql;
  execute v_sql;
end;
$$ language plpgsql;
CREATE FUNCTION
# 当表名, 列名中有空格或其他特殊字符时, 必须要用双引号, 因此以下调用将报错.

 

digoal=# select f_test('a b','d e','''');
NOTICE:  00000: create table a b(d e text)
LOCATION:  exec_stmt_raise, pl_exec.c:2985
ERROR:  42601: syntax error at or near "b"
LINE 1: create table a b(d e text)
                       ^
QUERY:  create table a b(d e text)
CONTEXT:  PL/pgSQL function f_test(text,text,text) line 7 at EXECUTE statement
LOCATION:  scanner_yyerror, scan.l:1044
# 当字符串中使用单引号时, 也必须逃逸, 使用双单引号. 这里也会报错.

 

digoal=# select f_test('ab','de','''');
NOTICE:  00000: create table ab(de text)
LOCATION:  exec_stmt_raise, pl_exec.c:2985
NOTICE:  00000: insert into ab(de) values (')
LOCATION:  exec_stmt_raise, pl_exec.c:2985
ERROR:  42601: unterminated quoted string at or near "')"
LINE 1: insert into ab(de) values (')
                                   ^
QUERY:  insert into ab(de) values (')
CONTEXT:  PL/pgSQL function f_test(text,text,text) line 10 at EXECUTE statement
LOCATION:  scanner_yyerror, scan.l:1044
 
使用quote : 
使用quote函数后, 数据库会自动根据需要帮你加上双引号和其他逃逸.

 

digoal=# create or replace function f_quote(i_tablename text, i_cname text, i_cval text) returns void as $$
declare 
  v_sql text;
begin
  v_sql := 'create table '||quote_ident(i_tablename)||'('||quote_ident(i_cname)||' text)';
  raise notice '%', v_sql;
  execute v_sql;
  v_sql := 'insert into '||quote_ident(i_tablename)||'('||quote_ident(i_cname)||') values ('||quote_literal(i_cval)||')';
  raise notice '%', v_sql;
  execute v_sql;
end;
$$ language plpgsql;
 
digoal=# select f_quote('a b','d e','''');
NOTICE:  00000: create table "a b"("d e" text)
LOCATION:  exec_stmt_raise, pl_exec.c:2985
NOTICE:  00000: insert into "a b"("d e") values ('''')
LOCATION:  exec_stmt_raise, pl_exec.c:2985
 f_quote 
---------
 
(1 row)
 
digoal=# select * from "a b";
 d e 
-----
 '
(1 row)

posted on 2013-06-05 12:29  OnlyPHP  阅读(1868)  评论(0编辑  收藏  举报

导航