From f53eab57b48713d58a9b6668a722ec796dfa52dd Mon Sep 17 00:00:00 2001 From: Mikhail Efremov Date: Tue, 29 Sep 2020 11:35:46 +0300 Subject: [PATCH] Add capture of request_body value to substitute in postgres_query --- README.md | 44 ++++++++ src/ngx_postgres_escape.c | 134 +++++++++++++++++++++- src/ngx_postgres_escape.h | 5 +- src/ngx_postgres_handler.c | 116 +++++++++++++++++-- src/ngx_postgres_handler.h | 2 + src/ngx_postgres_module.c | 223 +++++++++++++++++++++++++++++++++++++ src/ngx_postgres_module.h | 8 ++ t/request_body.t | 73 ++++++++++++ 8 files changed, 592 insertions(+), 13 deletions(-) create mode 100644 t/request_body.t diff --git a/README.md b/README.md index 3b50827..0e36473 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,32 @@ all empty strings are by default escaped to `NULL` value. This behavior can be disabled by prefixing `$unescaped` string with `=` sign. +postgres_escape_request_body +----------------------- +* **syntax**: `postgres_request_body_escape on/off` +* **default**: `on` +* **context**: `location` + +The flag provides to double each single quote symbol +in the complex SQL statements that contain substitutions from client http request body. +Disabling this option is pretty dangerous. + +Please make sure you are NOT using postgres configuration option `standard_conforming_strings: off` (default for PostgreSQL < 9.1). +Using `standard_conforming_strings: off` will make completely worthless the injection protection and allows an attacker to use control characters to change a query string. + +To check this option use: (correct setting below) +``` +postgres=# SHOW standard_conforming_strings; + +Set timeout for receiving result from the database. + + standard_conforming_strings +----------------------------- + on +(1 row) +``` + + postgres_connect_timeout ------------------------ * **syntax**: `postgres_connect_timeout timeout` @@ -357,6 +383,24 @@ Required modules (other than `ngx_postgres`): - [ngx_set_misc](http://github.com/agentzh/set-misc-nginx-module). + +Sample configuration #7 +----------------------- +Use POST data to parameter in SQL query. + + location /sms/(?\d+) { + postgres_pass database; + postgres_escape $id; + postgres_query POST "UPDATE sms SET sms_recipt = '$request_body' WHERE id=$id RETURNING 'ACK'"; + postgres_rewrite POST changes 200; + postgres_rewrite POST no_changes 410; + postgres_output value; + } + +The variable `request_body` cannot be processed by the `postgres_escape` directive during the rewrite phase. Therefore, for security reasons, the `request_body` value that is used in `postgres_query` is always processing to escape string literals during the content phase without the need for `postgres_escape`. + +This behavior can be disabled by directive `postgres_escape_request_body off`. + Testing ======= `ngx_postgres` comes with complete test suite based on [Test::Nginx](http://github.com/agentzh/test-nginx). diff --git a/src/ngx_postgres_escape.c b/src/ngx_postgres_escape.c index dd78194..1037cec 100644 --- a/src/ngx_postgres_escape.c +++ b/src/ngx_postgres_escape.c @@ -31,10 +31,11 @@ #include "ngx_postgres_ddebug.h" #include "ngx_postgres_escape.h" #include "ngx_postgres_module.h" +#include "ngx_postgres_util.h" #include - +#define SINGLE_QUOTE_CHAR 39 /* ASCII code of Apostrophe = 39 */ uintptr_t ngx_postgres_script_exit_code = (uintptr_t) NULL; @@ -95,3 +96,134 @@ ngx_postgres_escape_string(ngx_http_script_engine_t *e) v->no_cacheable = 0; v->not_found = 0; } + + +/* Purpose: calculate new length (with doubled quotes) for variable value. + * This method returns number of the single qoute characters (apostrophes) + * plus original length of text value */ +size_t +ngx_postgres_upstream_var_len_with_quotes(ngx_http_script_engine_t *e) +{ + size_t i; + size_t result; + size_t count; + ngx_http_script_var_code_t *code; + ngx_http_variable_value_t *value; + ngx_http_core_main_conf_t *cmcf; + ngx_http_variable_t *v; + ngx_http_request_t *r; + + dd("entering"); + result = 0; + count = 0; + + if ((e != NULL) && (e->ip != NULL) && (e->request != NULL)) { + code = (ngx_http_script_var_code_t *) e->ip; + e->ip += sizeof(ngx_http_script_var_code_t); + + if (!e->skip) { + if (e->flushed) { + value = ngx_http_get_indexed_variable(e->request, code->index); + + } else { + value = ngx_http_get_flushed_variable(e->request, code->index); + } + + if ((value != NULL) && (!value->not_found)) { + r = e->request; + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + v = cmcf->variables.elts; + + if (v[code->index].get_handler == + (ngx_http_get_variable_pt)ngx_postgres_rewrite_var) { + /* Quotes already escaped. Do nothing. */ + return value->len; + } + + for (i = 0; i < value->len; i++) { + if (value->data[i] == SINGLE_QUOTE_CHAR) { + count++; + } + } + + result = value->len + count; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, + 0, "http script: postgres: count to replace = %d", count); + } + } + } + dd("returning"); + + return result; +} + + +/* Purpose: replace quotes in variable values before substitute them in a query + * This method doubles the single qoute character (example: Can't -> Can''t) */ +void +ngx_postgres_upstream_replace_quotes(ngx_http_script_engine_t *e) +{ + int i; + int k; + u_char *p; + u_char *data; + ngx_http_script_var_code_t *code; + ngx_http_variable_value_t *value; + ngx_http_core_main_conf_t *cmcf; + ngx_http_variable_t *v; + ngx_http_request_t *r; + + dd("entering"); + + if ((e != NULL) && (e->ip != NULL) && (e->request != NULL)) { + code = (ngx_http_script_var_code_t *) e->ip; + + e->ip += sizeof(ngx_http_script_var_code_t); + + if (!e->skip) { + if (e->flushed) { + value = ngx_http_get_indexed_variable(e->request, code->index); + + } else { + value = ngx_http_get_flushed_variable(e->request, code->index); + } + + if ((value != NULL) && (!value->not_found) + && (e->buf.data != NULL)) { + p = e->pos; + data = value->data; + + r = e->request; + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + v = cmcf->variables.elts; + + if (v[code->index].get_handler == + (ngx_http_get_variable_pt) ngx_postgres_rewrite_var) { + /* Quotes already escaped. Do nothing. */ + return; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http script: postgres: replace quotes in $%V (\"%v\")", + &v[code->index].name, value); + + /* copy var value like ngx_http_script_copy_var_code(e) does */ + for (i = 0, k = 0; i < value->len; i++, k++) { + /* doubles all the qoute characters, + * do not double backslashes */ + if (data[i] == SINGLE_QUOTE_CHAR) { + p[k] = data[i]; + k++; + } + + p[k] = data[i]; + } + + e->pos += k; + } + } + } + + dd("returning"); +} + diff --git a/src/ngx_postgres_escape.h b/src/ngx_postgres_escape.h index 4312de6..150d3fd 100644 --- a/src/ngx_postgres_escape.h +++ b/src/ngx_postgres_escape.h @@ -31,6 +31,7 @@ #include -void ngx_postgres_escape_string(ngx_http_script_engine_t *); - +void ngx_postgres_escape_string(ngx_http_script_engine_t *); +size_t ngx_postgres_upstream_var_len_with_quotes(ngx_http_script_engine_t *e); +void ngx_postgres_upstream_replace_quotes(ngx_http_script_engine_t *e); #endif /* _NGX_POSTGRES_ESCAPE_H_ */ diff --git a/src/ngx_postgres_handler.c b/src/ngx_postgres_handler.c index 747a375..e5826d2 100644 --- a/src/ngx_postgres_handler.c +++ b/src/ngx_postgres_handler.c @@ -45,7 +45,6 @@ ngx_postgres_handler(ngx_http_request_t *r) ngx_postgres_ctx_t *pgctx; ngx_http_core_loc_conf_t *clcf; ngx_http_upstream_t *u; - ngx_connection_t *c; ngx_str_t host; ngx_url_t url; ngx_int_t rc; @@ -82,12 +81,6 @@ ngx_postgres_handler(ngx_http_request_t *r) return NGX_HTTP_INTERNAL_SERVER_ERROR; } - rc = ngx_http_discard_request_body(r); - if (rc != NGX_OK) { - dd("returning rc:%d", (int) rc); - return rc; - } - #if defined(nginx_version) \ && (((nginx_version >= 7063) && (nginx_version < 8000)) \ || (nginx_version >= 8007)) @@ -209,8 +202,110 @@ ngx_postgres_handler(ngx_http_request_t *r) r->main->count++; #endif - ngx_http_upstream_init(r); + rc = (ngx_int_t) ngx_postgres_read_req_body(r); + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "postgres: Where is a special response while reading request body. " + "Return code rc=%d", rc); + return rc; + } + + return NGX_DONE; +} + + +ngx_int_t +ngx_postgres_read_req_body(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_postgres_ctx_t *ctx; + rc = NGX_OK; + + if (r == NULL) { + /* postgres: Error. First argument (ngx_http_request_t *r) in function + ngx_postgres_read_req_body() can not be NULL (r = NULL) */ + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->request_body_in_single_buf = 1; + r->request_body_in_persistent_file = 1; + r->request_body_in_clean_file = 1; + +#if 1 + if (r->request_body_in_file_only) { + r->request_body_file_log_level = 0; + } +#endif + + ctx = ngx_http_get_module_ctx(r, ngx_postgres_module); + if (ctx == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "postgres: Error. Unable to get module context. " + "Method ngx_http_get_module_ctx(r, ngx_postgres_module) " + "returns ctx == NULL", rc); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "postgres: start to read buffered request body"); + + rc = ngx_http_read_client_request_body(r, ngx_postgres_body_handler); +#if (nginx_version < 1002006) || \ + (nginx_version >= 1003000 && nginx_version < 1003009) + r->main->count--; +#endif + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "postgres: http read client request body returned error code %i", rc); + + return NGX_DONE; + } + +#if (nginx_version >= 1002006 && nginx_version < 1003000) || \ + nginx_version >= 1003009 + r->main->count--; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "postgres: decrement r->main->count: %d", (int) r->main->count); +#endif + + if (rc == NGX_AGAIN) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "postgres: read buffered request body requires I/O interruptions"); + + ctx->waiting_more_body = 1; + return NGX_AGAIN; + } + + /* rc == NGX_OK */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "postgres: has read buffered request body in a single run"); + return NGX_DONE; +} + + +void +ngx_postgres_body_handler(ngx_http_request_t *r) +{ + ngx_postgres_ctx_t *ctx; + ngx_http_upstream_t *u; + ngx_connection_t *c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "postgres: req body post read, c:%ud", r->main->count); + + ctx = ngx_http_get_module_ctx(r, ngx_postgres_module); + + if (ctx->waiting_more_body) { + ctx->waiting_more_body = 0; + r->read_event_handler = ngx_http_block_reading; + } + + ngx_http_upstream_init(r); + u = r->upstream; /* override the read/write event handler to our own */ u->write_event_handler = ngx_postgres_wev_handler; u->read_event_handler = ngx_postgres_rev_handler; @@ -236,14 +331,15 @@ ngx_postgres_handler(ngx_http_request_t *r) #if defined(nginx_version) && (nginx_version >= 8017) NGX_HTTP_SERVICE_UNAVAILABLE); #else - pgctx->status ? pgctx->status : NGX_HTTP_INTERNAL_SERVER_ERROR); + ctx->status ? ctx->status : NGX_HTTP_INTERNAL_SERVER_ERROR); #endif } dd("returning NGX_DONE"); - return NGX_DONE; + return; } + void ngx_postgres_wev_handler(ngx_http_request_t *r, ngx_http_upstream_t *u) { diff --git a/src/ngx_postgres_handler.h b/src/ngx_postgres_handler.h index bbce47c..6af730e 100644 --- a/src/ngx_postgres_handler.h +++ b/src/ngx_postgres_handler.h @@ -34,6 +34,8 @@ ngx_int_t ngx_postgres_handler(ngx_http_request_t *); +ngx_int_t ngx_postgres_read_req_body(ngx_http_request_t *r); +void ngx_postgres_body_handler(ngx_http_request_t *r); void ngx_postgres_wev_handler(ngx_http_request_t *, ngx_http_upstream_t *); void ngx_postgres_rev_handler(ngx_http_request_t *, diff --git a/src/ngx_postgres_module.c b/src/ngx_postgres_module.c index 5c04b35..3809aec 100644 --- a/src/ngx_postgres_module.c +++ b/src/ngx_postgres_module.c @@ -120,6 +120,20 @@ static ngx_command_t ngx_postgres_module_commands[] = { offsetof(ngx_postgres_loc_conf_t, upstream.read_timeout), NULL }, + /* The flag add extra quote (as escape symbol) for each single quote symbol + * in the variable if the variable is substituted to the SQL. + * Setting disables the escape character + * substitution in the SQL string before single quotes. + * Disabling this option is pretty dangerous. + * + * default value: on */ + { ngx_string("postgres_escape_request_body"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_postgres_loc_conf_t, escape_request_body), + NULL }, + ngx_null_command }; @@ -228,6 +242,15 @@ ngx_postgres_output_enum_t ngx_postgres_output_handlers[] = { { ngx_null_string, 0, NULL } }; +static ngx_str_t request_body_var_name = ngx_string("request_body"); + +static ngx_int_t ngx_postgres_compile_with_quote_replace + (ngx_http_compile_complex_value_t *ccv); +static ngx_int_t ngx_postgres_add_quotes_escape_hook + (ngx_http_script_compile_t *sc); +static void ngx_postgres_replace_script_for_variable(ngx_array_t *array, + ngx_int_t var_index, ngx_http_script_code_pt old_value, + ngx_http_script_code_pt new_value); ngx_int_t ngx_postgres_add_variables(ngx_conf_t *cf) @@ -321,6 +344,7 @@ ngx_postgres_create_loc_conf(ngx_conf_t *cf) conf->rewrites = NGX_CONF_UNSET_PTR; conf->output_handler = NGX_CONF_UNSET_PTR; conf->variables = NGX_CONF_UNSET_PTR; + conf->escape_request_body = NGX_CONF_UNSET; /* the hardcoded values */ conf->upstream.cyclic_temp_file = 0; @@ -381,10 +405,22 @@ ngx_postgres_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_ptr_value(conf->variables, prev->variables, NULL); + if (conf->escape_request_body == NGX_CONF_UNSET) { + if (prev->escape_request_body == NGX_CONF_UNSET) { + /* default value: on */ + conf->escape_request_body = 1; + + } else { + /* merge */ + conf->escape_request_body = prev->escape_request_body; + } + } + dd("returning NGX_CONF_OK"); return NGX_CONF_OK; } + /* * Based on: ngx_http_upstream.c/ngx_http_upstream_server * Copyright (C) Igor Sysoev @@ -793,10 +829,25 @@ ngx_postgres_conf_query(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ccv.value = &sql; ccv.complex_value = query->cv; + if (pglcf->escape_request_body) { + /* Double the quote chars(') in values to prevent SQL injection */ + /* Add extra function handlers in the variable treatment byte-code*/ + if (ngx_postgres_compile_with_quote_replace(&ccv) != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "postgres: error while compile script bytecode to quote " + "replace in \"%V\" \"%V\" \"%V\" directive. The mechanism " + "activated by flag " + "works incorrectly.", &cmd->name, &value[1], &value[2]); + dd("returning NGX_CONF_ERROR"); + return NGX_CONF_ERROR; + } + + } else { if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { dd("returning NGX_CONF_ERROR"); return NGX_CONF_ERROR; } + } } else { /* simple value */ dd("simple value"); @@ -1336,3 +1387,175 @@ ngx_postgres_find_upstream(ngx_http_request_t *r, ngx_url_t *url) dd("returning NULL"); return NULL; } + + +/* Compile complex value with extra functions to double the quote chars(') */ +static ngx_int_t +ngx_postgres_compile_with_quote_replace(ngx_http_compile_complex_value_t *ccv) +{ + ngx_str_t *v; + ngx_uint_t i, n, nv, nc; + ngx_array_t flushes, lengths, values, *pf, *pl, *pv; + ngx_http_script_compile_t sc; + + v = ccv->value; + + nv = 0; + nc = 0; + + for (i = 0; i < v->len; i++) { + if (v->data[i] == '$') { + if (v->data[i + 1] >= '1' && v->data[i + 1] <= '9') { + nc++; + + } else { + nv++; + } + } + } + + if ((v->len == 0 || v->data[0] != '$') + && (ccv->conf_prefix || ccv->root_prefix)) + { + if (ngx_conf_full_name(ccv->cf->cycle, v, ccv->conf_prefix) != NGX_OK) { + return NGX_ERROR; + } + + ccv->conf_prefix = 0; + ccv->root_prefix = 0; + } + + ccv->complex_value->value = *v; + ccv->complex_value->flushes = NULL; + ccv->complex_value->lengths = NULL; + ccv->complex_value->values = NULL; + + if (nv == 0 && nc == 0) { + return NGX_OK; + } + + n = nv + 1; + + if (ngx_array_init(&flushes, ccv->cf->pool, n, sizeof(ngx_uint_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + n = nv * (2 * sizeof(ngx_http_script_copy_code_t) + + sizeof(ngx_http_script_var_code_t)) + + sizeof(uintptr_t); + + if (ngx_array_init(&lengths, ccv->cf->pool, n, 1) != NGX_OK) { + return NGX_ERROR; + } + + n = (nv * (2 * sizeof(ngx_http_script_copy_code_t) + + sizeof(ngx_http_script_var_code_t)) + + sizeof(uintptr_t) + + v->len + + sizeof(uintptr_t) - 1) + & ~(sizeof(uintptr_t) - 1); + + if (ngx_array_init(&values, ccv->cf->pool, n, 1) != NGX_OK) { + return NGX_ERROR; + } + + pf = &flushes; + pl = &lengths; + pv = &values; + + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = ccv->cf; + sc.source = v; + sc.flushes = &pf; + sc.lengths = &pl; + sc.values = &pv; + sc.complete_lengths = 1; + sc.complete_values = 1; + sc.zero = ccv->zero; + sc.conf_prefix = ccv->conf_prefix; + sc.root_prefix = ccv->root_prefix; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_ERROR; + } + + if (ngx_postgres_add_quotes_escape_hook(&sc) != NGX_OK) { + return NGX_ERROR; + } + + if (flushes.nelts) { + ccv->complex_value->flushes = flushes.elts; + ccv->complex_value->flushes[flushes.nelts] = (ngx_uint_t) -1; + } + + ccv->complex_value->lengths = lengths.elts; + ccv->complex_value->values = values.elts; + + return NGX_OK; +} + + +/* + * Add a hook to the script for copying variable values. + * The hook is needed to escape quotes in a variable request_body + * which gets value only during the content phase and + * cannot be handled by the postgres_escape directive during the rewite phase. + * + * Escaping quotes helps to prevent SQL injection + */ +static ngx_int_t +ngx_postgres_add_quotes_escape_hook(ngx_http_script_compile_t *sc) +{ + ngx_int_t var_index; + if ((sc != NULL) && (sc->lengths != NULL) && (sc->values != NULL)) { + + var_index = ngx_http_get_variable_index(sc->cf, &request_body_var_name); + + /* Replace code pointers with functions for processing sql query + * variables in bytecode. Mounted functions can replace single quote + * characters in var text. */ + ngx_postgres_replace_script_for_variable(*sc->lengths, var_index, + (ngx_http_script_code_pt)(void *)ngx_http_script_copy_var_len_code, + (ngx_http_script_code_pt)(void *) + ngx_postgres_upstream_var_len_with_quotes); + ngx_postgres_replace_script_for_variable(*sc->values, var_index, + (ngx_http_script_code_pt)(void *)ngx_http_script_copy_var_code, + (ngx_http_script_code_pt)(void *) + ngx_postgres_upstream_replace_quotes); + return NGX_OK; + } + + else { + return NGX_ERROR; + } +} + + +static void +ngx_postgres_replace_script_for_variable(ngx_array_t *array, + ngx_int_t var_index, ngx_http_script_code_pt old_value, + ngx_http_script_code_pt new_value) +{ + ngx_http_script_code_pt *cur; + ngx_http_script_code_pt *upper_bound; + ngx_http_script_code_pt code; + ngx_http_script_var_code_t *var_code; + size_t i; + + upper_bound = (ngx_http_script_code_pt *) array->elts + array->nalloc; + cur = (ngx_http_script_code_pt *) array->elts; + + for (i = 0; (ngx_uint_t)&cur[i] < (ngx_uint_t)upper_bound; i++) { + code = cur[i]; + if (code == old_value) { + var_code = (ngx_http_script_var_code_t *)&cur[i]; + if ((ngx_int_t) var_code->index == var_index) + { + cur[i] = new_value; + } + } + } +} diff --git a/src/ngx_postgres_module.h b/src/ngx_postgres_module.h index b0a6378..5beda4f 100644 --- a/src/ngx_postgres_module.h +++ b/src/ngx_postgres_module.h @@ -176,6 +176,10 @@ typedef struct { unsigned output_binary:1; /* custom variables */ ngx_array_t *variables; + /* request body substitution mode */ + /* on - adds escape symbols to quote symbols */ + /* off - copy request body into sql with no changes */ + ngx_flag_t escape_request_body; } ngx_postgres_loc_conf_t; @@ -187,6 +191,10 @@ typedef struct { ngx_str_t var_query; ngx_array_t *variables; ngx_int_t status; + /* Flag to save state of request body recive + * true - waiting for more request body data; + * false - no need to wait */ + ngx_flag_t waiting_more_body:1; } ngx_postgres_ctx_t; diff --git a/t/request_body.t b/t/request_body.t new file mode 100644 index 0000000..5698503 --- /dev/null +++ b/t/request_body.t @@ -0,0 +1,73 @@ +# vi:filetype=perl + +use lib 'lib'; +use Test::Nginx::Socket; + +plan tests => repeat_each() * (2 + 2 + 1); + +$ENV{TEST_NGINX_POSTGRESQL_HOST} ||= '127.0.0.1'; +$ENV{TEST_NGINX_POSTGRESQL_PORT} ||= 5432; + +our $http_config = <<'_EOC_'; + upstream database { + postgres_server $TEST_NGINX_POSTGRESQL_HOST:$TEST_NGINX_POSTGRESQL_PORT + dbname=ngx_test user=ngx_test password=ngx_test; + } +_EOC_ + +no_shuffle(); +run_tests(); + +__DATA__ + +=== TEST 1: capture request body +--- http_config eval: $::http_config +--- config + location /test { + postgres_pass database; + postgres_query POST "SELECT '$request_body'"; + postgres_output value; + } +--- request eval +"POST /test +{ test: \"My simple request\" }" +--- error_code: 200 +--- response_body eval +"{ test: \"My simple request\" }" +--- timeout: 10 + + + +=== TEST 2: escape request body +--- http_config eval: $::http_config +--- config + location /test { + postgres_pass database; + postgres_query POST "SELECT '$request_body'"; + postgres_output value; + } +--- request eval +"POST /test +'; SQL injection attempt;" +--- error_code: 200 +--- response_body eval +"'; SQL injection attempt;" +--- timeout: 10 + + + +=== TEST 3: escape request body disabled +--- http_config eval: $::http_config +--- config + location /test { + postgres_escape_request_body off; + postgres_pass database; + postgres_query POST "SELECT '$request_body'"; + postgres_output value; + } +--- request eval +"POST /test +'; SQL injection attempt;--" +--- error_code: 500 +--- timeout: 10 +