Implemented SecRequestBodyNoFilesLimit (#103).

This commit is contained in:
ivanr 2007-11-29 11:41:48 +00:00
parent fd5e4fb32c
commit 575e86388a
7 changed files with 103 additions and 7 deletions

View File

@ -1,7 +1,9 @@
26 Nov 2007 - 2.5.0-dev3
29 Nov 2007 - 2.5.0-dev3
------------------------
* Implemented SecRequestBodyNoFilesLimit.
* Enhance handling of the case where we run out of disk space while
writing to audit log entry.

View File

@ -34,6 +34,7 @@ void *create_directory_config(apr_pool_t *mp, char *path) {
dcfg->reqbody_access = NOT_SET;
dcfg->reqbody_inmemory_limit = NOT_SET;
dcfg->reqbody_limit = NOT_SET;
dcfg->reqbody_no_files_limit = NOT_SET;
dcfg->resbody_access = NOT_SET;
dcfg->debuglog_name = NOT_SET_P;
@ -221,6 +222,8 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) {
? parent->reqbody_inmemory_limit : child->reqbody_inmemory_limit);
merged->reqbody_limit = (child->reqbody_limit == NOT_SET
? parent->reqbody_limit : child->reqbody_limit);
merged->reqbody_no_files_limit = (child->reqbody_no_files_limit == NOT_SET
? parent->reqbody_no_files_limit : child->reqbody_no_files_limit);
merged->resbody_access = (child->resbody_access == NOT_SET
? parent->resbody_access : child->resbody_access);
@ -453,6 +456,7 @@ void init_directory_config(directory_config *dcfg) {
if (dcfg->reqbody_inmemory_limit == NOT_SET)
dcfg->reqbody_inmemory_limit = REQUEST_BODY_DEFAULT_INMEMORY_LIMIT;
if (dcfg->reqbody_limit == NOT_SET) dcfg->reqbody_limit = REQUEST_BODY_DEFAULT_LIMIT;
if (dcfg->reqbody_no_files_limit == NOT_SET) dcfg->reqbody_no_files_limit = REQUEST_BODY_NO_FILES_DEFAULT_LIMIT;
if (dcfg->resbody_access == NOT_SET) dcfg->resbody_access = 0;
if (dcfg->of_limit == NOT_SET) dcfg->of_limit = RESPONSE_BODY_DEFAULT_LIMIT;
if (dcfg->of_limit_action == NOT_SET) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT;
@ -1022,6 +1026,22 @@ static const char *cmd_request_body_limit(cmd_parms *cmd, void *_dcfg, const cha
return NULL;
}
static const char *cmd_request_body_no_files_limit(cmd_parms *cmd, void *_dcfg, const char *p1) {
directory_config *dcfg = (directory_config *)_dcfg;
long int limit;
if (dcfg == NULL) return NULL;
limit = strtol(p1, NULL, 10);
if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyNoFilesLimit: %s", p1);
}
dcfg->reqbody_no_files_limit = limit;
return NULL;
}
static const char *cmd_request_body_access(cmd_parms *cmd, void *_dcfg, const char *p1) {
directory_config *dcfg = (directory_config *)_dcfg;
if (dcfg == NULL) return NULL;
@ -1668,7 +1688,15 @@ const command_rec module_directives[] = {
cmd_request_body_limit,
NULL,
CMD_SCOPE_ANY,
"maximum request body size ModSecurity is allowed to access."
"maximum request body size ModSecurity will accept."
),
AP_INIT_TAKE1 (
"SecRequestBodyNoFilesLimit",
cmd_request_body_no_files_limit,
NULL,
CMD_SCOPE_ANY,
"maximum request body size ModSecurity will accept, but excluding the size of uploaded files."
),
AP_INIT_TAKE1 (

View File

@ -221,7 +221,14 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) {
}
if (buflen != 0) {
if (modsecurity_request_body_store(msr, buf, buflen, error_msg) < 0) {
int rcbs = modsecurity_request_body_store(msr, buf, buflen, error_msg);
if (rcbs < 0) {
if (rcbs == -5) {
*error_msg = apr_psprintf(msr->mp, "Requests body no files data length is larger than the "
"configured limit (%lu).", msr->txcfg->reqbody_no_files_limit);
return -5;
}
return -1;
}

View File

@ -107,6 +107,7 @@ extern DSOLOCAL modsec_build_type_rec modsec_build_type[];
#define REQUEST_BODY_HARD_LIMIT 1073741824L
#define REQUEST_BODY_DEFAULT_INMEMORY_LIMIT 131072
#define REQUEST_BODY_DEFAULT_LIMIT 134217728
#define REQUEST_BODY_NO_FILES_DEFAULT_LIMIT 1048576
#define RESPONSE_BODY_DEFAULT_LIMIT 524288
#define RESPONSE_BODY_HARD_LIMIT 1073741824L
@ -306,6 +307,8 @@ struct modsec_rec {
int msc_reqbody_error;
const char *msc_reqbody_error_msg;
apr_size_t msc_reqbody_no_files_length;
multipart_data *mpd; /* MULTIPART processor data structure */
#ifdef WITH_LIBXML2
@ -366,6 +369,7 @@ struct directory_config {
int reqbody_access;
long int reqbody_inmemory_limit;
long int reqbody_limit;
long int reqbody_no_files_limit;
int resbody_access;
long int of_limit;

View File

@ -192,6 +192,9 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
}
}
/* The buffer is data so increase the data length counter. */
msr->msc_reqbody_no_files_length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft);
if (len > 1) {
if (msr->mpd->buf[len - 2] == '\r') {
msr->mpd->flag_crlf_line = 1;
@ -422,6 +425,9 @@ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) {
}
else if (msr->mpd->mpp->type == MULTIPART_FORMDATA) {
value_part_t *value_part = apr_pcalloc(msr->mp, sizeof(value_part_t));
/* The buffer contains data so increase the data length counter. */
msr->msc_reqbody_no_files_length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0];
/* add this part to the list of parts */

View File

@ -25,7 +25,6 @@ static apr_status_t modsecurity_request_body_start_init(modsec_rec *msr, char **
msr->msc_reqbody_chunks = apr_array_make(msr->msc_reqbody_mp,
32, sizeof(msc_data_chunk *));
if (msr->msc_reqbody_chunks == NULL) {
*error_msg = apr_pstrdup(msr->mp, "Input filter: Failed to prepare in-memory storage.");
return -1;
}
@ -263,6 +262,11 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr,
char *my_error_msg = NULL;
if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) {
/* The per-request data length counter will
* be updated by the multipart parser.
*/
/* Process data as multipart/form-data. */
if (multipart_process_chunk(msr, data, length, &my_error_msg) < 0) {
*error_msg = apr_psprintf(msr->mp, "Request body processor error: %s", my_error_msg);
msr->msc_reqbody_error = 1;
@ -273,6 +277,10 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr,
#ifdef WITH_LIBXML2
else
if (strcmp(msr->msc_reqbody_processor, "XML") == 0) {
/* Increase per-request data length counter. */
msr->msc_reqbody_no_files_length += length;
/* Process data as XML. */
if (xml_process_chunk(msr, data, length, &my_error_msg) < 0) {
*error_msg = apr_psprintf(msr->mp, "Request body processor error: %s", my_error_msg);
msr->msc_reqbody_error = 1;
@ -283,7 +291,10 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr,
#endif
else
if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) {
/* Do nothing, URLENCODED processor does not support streaming. */
/* Increase per-request data length counter. */
msr->msc_reqbody_no_files_length += length;
/* Do nothing else, URLENCODED processor does not support streaming. */
}
else {
*error_msg = apr_psprintf(msr->mp, "Unknown request body processor: %s",
@ -291,6 +302,11 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr,
return -1;
}
}
/* Check that we are not over the request body no files limit. */
if (msr->msc_reqbody_no_files_length >= (unsigned long) msr->txcfg->reqbody_no_files_limit) {
return -5;
}
/* Store data. */
if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) {
@ -395,8 +411,10 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) {
}
}
/* Note that we've read the body. */
msr->msc_reqbody_read = 1;
/* Finalise body processing. */
if ((msr->msc_reqbody_processor != NULL)&&(msr->msc_reqbody_error == 0)) {
char *my_error_msg = NULL;
@ -432,6 +450,9 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) {
#endif
}
/* Note the request body no files length. */
msr_log(msr, 4, "Reqest body no files length: %lu", msr->msc_reqbody_no_files_length);
return 1;
}

View File

@ -1278,8 +1278,6 @@ SecAuditLogStorageDir logs/audit
<para><emphasis>Example Usage:</emphasis> <literal
moreinfo="none">SecRequestBodyLimit 134217728</literal></para>
<para><emphasis>Processing Phase:</emphasis> N/A</para>
<para><emphasis>Scope:</emphasis> Any</para>
<para><emphasis>Dependencies/Notes:</emphasis> 131072 KB (134217728
@ -1287,6 +1285,36 @@ SecAuditLogStorageDir logs/audit
with status code 413 Request Entity Too Large. There is a hard limit of
1 GB.</para>
</section>
<section>
<title><literal>SecRequestBodyNoFilesLimit</literal></title>
<para><emphasis>Description:</emphasis> Configures the maximum request
body size ModSecurity will accept for buffering, excluding the size of
files being transported in the request. This directive comes handy to
further reduce susceptability to DoS attacks when someone is sending
request bodies of very large sizes. Web applications that require file
uploads must configure <literal>SecRequestBodyLimit</literal> to a
high value. Since large files are streamed to disk file uploads will
not increase memory consumption. However, it's still possible for
someone to take advantage of a large request body limit and send
non-upload requests with large body sizes. This directive eliminates
that loophole. </para>
<para><emphasis>Syntax:</emphasis> <literal
moreinfo="none">SecRequestBodyNoFilesLimit NUMBER_IN_BYTES</literal></para>
<para><emphasis>Example Usage:</emphasis> <literal
moreinfo="none">SecRequestBodyLimit 131072</literal></para>
<para><emphasis>Scope:</emphasis> Any</para>
<para><emphasis>Dependencies/Notes:</emphasis> 1 MB (1048576
bytes) is the default setting. This value is very conservative. For
most applications you should be able to reduce it down to 128 KB or
lower. Anything over the limit will be rejected with status code <literal>413
Request Entity Too Large</literal>. There is a hard limit of 1 GB.</para>
</section>
<section>
<title><literal>SecRequestBodyInMemoryLimit</literal></title>