diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index e02b0c8..1b165ea 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -1284,6 +1284,22 @@ include_dir 'conf.d' + + disallow_multiple_queries (boolean) + + disallow_multiple_queries configuration parameter + + + + + When this parameter is on, the PostgreSQL server + disallow multiple SQL commands in the given string unless it is + transaction block in simple query protocol. It is useful for providing + an additional defense against SQL-injection attacks. + + + + diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 75c2d9a..70f6392 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -96,6 +96,7 @@ int max_stack_depth = 100; /* wait N seconds to allow attach from a debugger */ int PostAuthDelay = 0; +bool disallow_multiple_queries = false; /* ---------------- @@ -937,6 +938,33 @@ exec_simple_query(const char *query_string) */ parsetree_list = pg_parse_query(query_string); + if (disallow_multiple_queries && (list_length(parsetree_list) > 1)) + { + const char *commandTagHead; + const char *commandTagTail; + Node *parsetreehead; + Node *parsetreetail; + /* + * we use head and tail cell for checking weather it is a transaction + * block or not + */ + parsetreehead = (Node *) lfirst(list_head(parsetree_list)); + parsetreetail = (Node *) lfirst(list_tail(parsetree_list)); + /* + * figure out head and tail command type + */ + commandTagHead = CreateCommandTag(parsetreehead); + commandTagTail = CreateCommandTag(parsetreetail); + /* + * We only allow a single user query or a transaction block per call . + * This is provide an additional defense against SQL-injection attacks. + */ + + if ((strcmp(commandTagHead, "BEGIN") != 0) || (strcmp(commandTagTail, "COMMIT") != 0) ) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot execute multiple commands unless it is a transaction block"))); + } + /* Log immediately if dictated by log_statement */ if (check_log_statement(parsetree_list)) { diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index a414fb2..0f9617c 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1670,6 +1670,16 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, + { + {"disallow_multiple_queries", PGC_POSTMASTER, CLIENT_CONN_OTHER, + gettext_noop("Disallow multiple queries per query string."), + NULL + }, + &disallow_multiple_queries, + false, + NULL, NULL, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index f1a34a1..c81bbae 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -46,6 +46,7 @@ typedef enum } LogStmtLevel; extern int log_statement; +extern bool disallow_multiple_queries; extern List *pg_parse_query(const char *query_string); extern List *pg_analyze_and_rewrite(RawStmt *parsetree,