mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-08-14 05:45:59 +03:00
Adds REQUEST_FULL and REQUEST_FULL_LENGTH variables
This variable is a combination from REQUEST_LINE, REQUEST_HEADERS and REQUEST_BODY (if any). Expects for \n\n in between each of those values.
This commit is contained in:
parent
62f3d02894
commit
d93ce9ceee
@ -519,6 +519,8 @@ static modsec_rec *create_tx_context(request_rec *r) {
|
|||||||
msr->request_headers = apr_table_copy(msr->mp, r->headers_in);
|
msr->request_headers = apr_table_copy(msr->mp, r->headers_in);
|
||||||
msr->hostname = ap_get_server_name(r);
|
msr->hostname = ap_get_server_name(r);
|
||||||
|
|
||||||
|
msr->msc_full_request_buffer = NULL;
|
||||||
|
msr->msc_full_request_length = 0;
|
||||||
msr->msc_rule_mptmp = NULL;
|
msr->msc_rule_mptmp = NULL;
|
||||||
|
|
||||||
/* Invoke the engine to continue with initialisation */
|
/* Invoke the engine to continue with initialisation */
|
||||||
|
@ -261,6 +261,12 @@ static apr_status_t modsecurity_tx_cleanup(void *data) {
|
|||||||
msr_log(msr, 1, "%s", my_error_msg);
|
msr_log(msr, 1, "%s", my_error_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msr->msc_full_request_length > 0 &&
|
||||||
|
msr->msc_full_request_buffer != NULL) {
|
||||||
|
msr->msc_full_request_length = 0;
|
||||||
|
free(msr->msc_full_request_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(WITH_LUA)
|
#if defined(WITH_LUA)
|
||||||
#ifdef CACHE_LUA
|
#ifdef CACHE_LUA
|
||||||
if(msr->L != NULL) lua_close(msr->L);
|
if(msr->L != NULL) lua_close(msr->L);
|
||||||
|
@ -355,6 +355,9 @@ struct modsec_rec {
|
|||||||
|
|
||||||
apr_size_t msc_reqbody_no_files_length;
|
apr_size_t msc_reqbody_no_files_length;
|
||||||
|
|
||||||
|
char *msc_full_request_buffer;
|
||||||
|
int msc_full_request_length;
|
||||||
|
|
||||||
char *multipart_filename;
|
char *multipart_filename;
|
||||||
char *multipart_name;
|
char *multipart_name;
|
||||||
multipart_data *mpd; /* MULTIPART processor data structure */
|
multipart_data *mpd; /* MULTIPART processor data structure */
|
||||||
|
@ -2386,3 +2386,69 @@ char *construct_single_var(modsec_rec *msr, char *name) {
|
|||||||
|
|
||||||
return (char *)vx->value;
|
return (char *)vx->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Transforms an apr_array_header_t to a text buffer
|
||||||
|
*
|
||||||
|
* Converts an apr_array_header_t into a plain/text buffer in a Key: Pair
|
||||||
|
* format. The generated buffer is not null terminated.
|
||||||
|
*
|
||||||
|
* If called with `buffer_length` set to 0 or with `buffer` set to NULL,
|
||||||
|
* it will _not_ fill any buffer, instead, it will return the length, that
|
||||||
|
* will be needed to save the entire content of `arr` into a buffer.
|
||||||
|
*
|
||||||
|
* @warning return is not NULL-terminated.
|
||||||
|
* @note memory management is in the responsibility of the caller.
|
||||||
|
*
|
||||||
|
* @param arr apr_array_header_t to be iterated.
|
||||||
|
* @param buffer pointer to the destination buffer.
|
||||||
|
* @param buffer_length length that will fully fill the buffer.
|
||||||
|
* @retval -1 Something went wrong in the process. Do not trust in
|
||||||
|
* buffer content.
|
||||||
|
* @retval n>0 size of the [needed|] buffer.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int msc_headers_to_buffer(const apr_array_header_t *arr, char *buffer,
|
||||||
|
int buffer_length)
|
||||||
|
{
|
||||||
|
int headers_length = 0;
|
||||||
|
int write_to_buffer = 0;
|
||||||
|
int i = 0;
|
||||||
|
const apr_table_entry_t *te = NULL;
|
||||||
|
|
||||||
|
if (buffer != NULL && buffer_length > 0) {
|
||||||
|
write_to_buffer = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
te = (apr_table_entry_t *)arr->elts;
|
||||||
|
for (i = 0; i < arr->nelts; i++) {
|
||||||
|
char *value = te[i].val;
|
||||||
|
char *key = te[i].key;
|
||||||
|
headers_length = headers_length + strlen(value) + strlen(key) + /* \n: */ 1 +
|
||||||
|
/* colum */ 1 + /* space: */ 1 ;
|
||||||
|
|
||||||
|
if (write_to_buffer == 1) {
|
||||||
|
if (buffer_length < headers_length) {
|
||||||
|
headers_length = -1;
|
||||||
|
goto not_enough_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(buffer, "%s%s: %s\n", buffer, key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
headers_length++; /* Save space for an extra '\n' between the hedaers and the request body */
|
||||||
|
if (write_to_buffer) {
|
||||||
|
if (buffer_length < headers_length) {
|
||||||
|
headers_length = -1;
|
||||||
|
goto not_enough_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[headers_length-1] = '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
not_enough_memory:
|
||||||
|
return headers_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -149,4 +149,6 @@ char DSOLOCAL *format_all_performance_variables(modsec_rec *msr, apr_pool_t *mp)
|
|||||||
unsigned char DSOLOCAL is_netmask_v4(char *ip_strv4);
|
unsigned char DSOLOCAL is_netmask_v4(char *ip_strv4);
|
||||||
|
|
||||||
unsigned char DSOLOCAL is_netmask_v6(char *ip_strv6);
|
unsigned char DSOLOCAL is_netmask_v6(char *ip_strv6);
|
||||||
|
|
||||||
|
int DSOLOCAL msc_headers_to_buffer(const apr_array_header_t *arr, char *buffer, int max_length);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1934,6 +1934,87 @@ static int var_request_basename_generate(modsec_rec *msr, msre_var *var, msre_ru
|
|||||||
return var_simple_generate(var, vartab, mptmp, value);
|
return var_simple_generate(var, vartab, mptmp, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FULL_REQUEST */
|
||||||
|
|
||||||
|
static int var_full_request_generate(modsec_rec *msr, msre_var *var,
|
||||||
|
msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp)
|
||||||
|
{
|
||||||
|
const apr_array_header_t *arr = NULL;
|
||||||
|
char *full_request = NULL;
|
||||||
|
int full_request_length = 0;
|
||||||
|
int headers_length = 0;
|
||||||
|
int request_line_length = 0;
|
||||||
|
|
||||||
|
arr = apr_table_elts(msr->request_headers);
|
||||||
|
headers_length = msc_headers_to_buffer(arr, NULL, 0);
|
||||||
|
if (headers_length < 0) {
|
||||||
|
msr_log(msr, 9, "Variable FULL_REQUEST failed. Problems to measure " \
|
||||||
|
"headers length.");
|
||||||
|
goto failed_measure_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
request_line_length = strlen(msr->request_line) + /* \n\n: */ 2;
|
||||||
|
full_request_length = request_line_length + headers_length +
|
||||||
|
msr->msc_reqbody_length + /* \0: */1;
|
||||||
|
|
||||||
|
full_request = malloc(sizeof(char)*full_request_length);
|
||||||
|
if (full_request == NULL) {
|
||||||
|
if (msr->txcfg->debuglog_level >= 9) {
|
||||||
|
msr_log(msr, 8, "Variable FULL_REQUEST will not be created, not " \
|
||||||
|
"enough memory available.");
|
||||||
|
}
|
||||||
|
goto failed_not_enough_mem;
|
||||||
|
}
|
||||||
|
memset(full_request, '\0', sizeof(char)*msr->msc_full_request_length);
|
||||||
|
msr->msc_full_request_buffer = full_request;
|
||||||
|
msr->msc_full_request_length = full_request_length;
|
||||||
|
|
||||||
|
apr_snprintf(full_request, request_line_length + 1,
|
||||||
|
/* +1 here because sprintf will place \0 in the end of the string.*/
|
||||||
|
"%s\n\n", msr->request_line);
|
||||||
|
|
||||||
|
headers_length = msc_headers_to_buffer(arr, full_request +
|
||||||
|
request_line_length, headers_length);
|
||||||
|
if (headers_length < 0) {
|
||||||
|
msr_log(msr, 9, "Variable FULL_REQUEST will not be created, failed " \
|
||||||
|
"to fill headers buffer.");
|
||||||
|
goto failed_fill_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msr->msc_reqbody_length > 0 && msr->msc_reqbody_buffer != NULL) {
|
||||||
|
memcpy(full_request + (headers_length + request_line_length),
|
||||||
|
msr->msc_reqbody_buffer, msr->msc_reqbody_length);
|
||||||
|
}
|
||||||
|
full_request[msr->msc_full_request_length-1] = '\0';
|
||||||
|
|
||||||
|
return var_simple_generate_ex(var, vartab, mptmp, full_request,
|
||||||
|
msr->msc_full_request_length);
|
||||||
|
|
||||||
|
failed_fill_buffer:
|
||||||
|
failed_not_enough_mem:
|
||||||
|
failed_measure_buffer:
|
||||||
|
no_buffer:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FULL_REQUEST_LENGTH */
|
||||||
|
|
||||||
|
static int var_full_request_length_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||||
|
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||||
|
{
|
||||||
|
const apr_array_header_t *arr = NULL;
|
||||||
|
char *value = NULL;
|
||||||
|
int headers_length = 0;
|
||||||
|
|
||||||
|
arr = apr_table_elts(msr->request_headers);
|
||||||
|
headers_length = msc_headers_to_buffer(arr, NULL, 0);
|
||||||
|
msr->msc_full_request_length = headers_length + msr->msc_reqbody_length + /* \0: */1;
|
||||||
|
|
||||||
|
value = apr_psprintf(mptmp, "%d", msr->msc_full_request_length);
|
||||||
|
return var_simple_generate(var, vartab, mptmp, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* REQUEST_BODY */
|
/* REQUEST_BODY */
|
||||||
|
|
||||||
static int var_request_body_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
static int var_request_body_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||||
@ -3204,12 +3285,35 @@ void msre_engine_register_default_variables(msre_engine *engine) {
|
|||||||
PHASE_REQUEST_HEADERS
|
PHASE_REQUEST_HEADERS
|
||||||
);
|
);
|
||||||
|
|
||||||
/* REQUEST_BODY */
|
/* FULL_REQUEST */
|
||||||
msre_engine_variable_register(engine,
|
msre_engine_variable_register(engine,
|
||||||
"REQUEST_BODY",
|
"FULL_REQUEST",
|
||||||
VAR_SIMPLE,
|
VAR_SIMPLE,
|
||||||
0, 0,
|
0, 0,
|
||||||
NULL,
|
NULL,
|
||||||
|
var_full_request_generate,
|
||||||
|
VAR_CACHE,
|
||||||
|
PHASE_REQUEST_BODY
|
||||||
|
);
|
||||||
|
|
||||||
|
/* FULL_REQUEST_LENGTH */
|
||||||
|
msre_engine_variable_register(engine,
|
||||||
|
"FULL_REQUEST_LENGTH",
|
||||||
|
VAR_SIMPLE,
|
||||||
|
0, 0,
|
||||||
|
NULL,
|
||||||
|
var_full_request_length_generate,
|
||||||
|
VAR_CACHE,
|
||||||
|
PHASE_REQUEST_BODY
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/* REQUEST_BODY */
|
||||||
|
msre_engine_variable_register(engine,
|
||||||
|
"REQUEST_BODY",
|
||||||
|
VAR_LIST,
|
||||||
|
0, 1,
|
||||||
|
var_generic_list_validate,
|
||||||
var_request_body_generate,
|
var_request_body_generate,
|
||||||
VAR_CACHE,
|
VAR_CACHE,
|
||||||
PHASE_REQUEST_BODY
|
PHASE_REQUEST_BODY
|
||||||
|
@ -373,6 +373,110 @@
|
|||||||
"arg1=val1&arg2=val2",
|
"arg1=val1&arg2=val2",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
# FULL_REQUEST
|
||||||
|
{
|
||||||
|
type => "target",
|
||||||
|
comment => "FULL_REQUEST (get)",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecRequestBodyAccess On
|
||||||
|
SecResponseBodyAccess On
|
||||||
|
SecDebugLog $ENV{DEBUG_LOG}
|
||||||
|
SecDebugLogLevel 9
|
||||||
|
SecRule FULL_REQUEST "arg1" "phase:4,log,pass,id:500211"
|
||||||
|
SecRule FULL_REQUEST "arg2" "phase:4,log,pass,id:500212"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
error => [ qr/Pattern match "arg1" at FULL_REQUEST.*Pattern match "arg2" at FULL_REQUEST/s, 1 ],
|
||||||
|
debug => [ qr/against FULL_REQUEST.*Target value: "GET \/test.txt\?arg1=val1\&arg2=val2 HTTP\/1.1\\n\\nTE: deflate,gzip;q=0.3\\nConnection: TE, close\\nHost: localhost:8088\\nUser-Agent: ModSecurity Regression Tests\/1.2.3\\n\\n\\x00"/s, 1],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => "target",
|
||||||
|
comment => "FULL_REQUEST (post)",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecRequestBodyAccess On
|
||||||
|
SecResponseBodyAccess On
|
||||||
|
SecDebugLog $ENV{DEBUG_LOG}
|
||||||
|
SecDebugLogLevel 9
|
||||||
|
SecRule FULL_REQUEST "arg1" "phase:4,log,pass,id:500213"
|
||||||
|
SecRule FULL_REQUEST "arg2" "phase:4,log,pass,id:500214"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
error => [ qr/Pattern match "arg1" at FULL_REQUEST.*Pattern match "arg2" at FULL_REQUEST/s, 1 ],
|
||||||
|
debug => [ qr/against FULL_REQUEST.*Target value: "POST \/test.txt HTTP\/1.1\\n\\nTE: deflate,gzip;q=0.3\\nConnection: TE, close\\nHost: localhost:8088\\nUser-Agent: ModSecurity Regression Tests\/1.2.3\\nContent-Type: application\/x-www-form-urlencoded\\nContent-Length: 19\\n\\narg1=val1&arg2=val2\\x00"/s, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
[
|
||||||
|
"Content-Type" => "application/x-www-form-urlencoded",
|
||||||
|
],
|
||||||
|
"arg1=val1&arg2=val2",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
# FULL_REQUEST_LENGTH
|
||||||
|
{
|
||||||
|
type => "target",
|
||||||
|
comment => "FULL_REQUEST_LENGTH (get)",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecRequestBodyAccess On
|
||||||
|
SecResponseBodyAccess On
|
||||||
|
SecDebugLog $ENV{DEBUG_LOG}
|
||||||
|
SecDebugLogLevel 9
|
||||||
|
SecRule FULL_REQUEST_LENGTH "\@eq 1" "phase:4,log,pass,id:500211"
|
||||||
|
SecRule FULL_REQUEST_LENGTH "\@eq 115" "phase:4,log,pass,id:500212"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
error => [ qr/Operator EQ matched 115 at FULL_REQUEST_LENGTH./s, 1 ],
|
||||||
|
debug => [ qr/against FULL_REQUEST_LENGTH.*Target value: "115"/s, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => "target",
|
||||||
|
comment => "FULL_REQUEST_LENGTH (post)",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecRequestBodyAccess On
|
||||||
|
SecResponseBodyAccess On
|
||||||
|
SecDebugLog $ENV{DEBUG_LOG}
|
||||||
|
SecDebugLogLevel 9
|
||||||
|
SecRule FULL_REQUEST_LENGTH "\@eq 1" "phase:4,log,pass,id:500213"
|
||||||
|
SecRule FULL_REQUEST_LENGTH "\@eq 201" "phase:4,log,pass,id:500214"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
error => [ qr/Operator EQ matched 201 at FULL_REQUEST_LENGTH./s, 1 ],
|
||||||
|
debug => [ qr/against FULL_REQUEST_LENGTH.*Target value: "201"/s, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
[
|
||||||
|
"Content-Type" => "application/x-www-form-urlencoded",
|
||||||
|
],
|
||||||
|
"arg1=val1&arg2=val2",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# AUTH_TYPE
|
# AUTH_TYPE
|
||||||
#{
|
#{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user