Skip to content

Fix processing of response body when gzip compression is enabled #107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 58 additions & 13 deletions config
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,30 @@ fi

ngx_addon_name=ngx_http_modsecurity_module

# We must place ngx_http_modsecurity_module after ngx_http_gzip_filter_module
# in load order list to be able to read response body before it gets compressed
# (for filter modules later initialization means earlier execution).
#
# Nginx implements load ordering only for dynamic modules and only a BEFORE part
# of "ngx_module_order". So we list all of the modules that come after
# ngx_http_gzip_filter_module as a BEFORE dependency for
# ngx_http_modsecurity_module.
#
# For static compilation HTTP_FILTER_MODULES will be patched later.

modsecurity_dependency="ngx_http_postpone_filter_module \
ngx_http_ssi_filter_module \
ngx_http_charset_filter_module \
ngx_http_xslt_filter_module \
ngx_http_image_filter_module \
ngx_http_sub_filter_module \
ngx_http_addition_filter_module \
ngx_http_gunzip_filter_module \
ngx_http_userid_filter_module \
ngx_http_headers_filter_module \
ngx_http_copy_filter_module"


if test -n "$ngx_module_link"; then
ngx_module_type=HTTP_FILTER
ngx_module_name="$ngx_addon_name"
Expand All @@ -98,7 +122,12 @@ if test -n "$ngx_module_link"; then
ngx_module_libs="$ngx_feature_libs"
ngx_module_incs="$ngx_feature_path"

ngx_module_order="ngx_http_chunked_filter_module ngx_http_v2_filter_module $ngx_module_name ngx_http_range_header_filter_module"
ngx_module_order="ngx_http_chunked_filter_module \
ngx_http_v2_filter_module \
ngx_http_range_header_filter_module \
ngx_http_gzip_filter_module \
$ngx_module_name \
$modsecurity_dependency";

. auto/module
else
Expand Down Expand Up @@ -128,20 +157,36 @@ fi

#
# Nginx does not provide reliable way to introduce our module into required
# place in static ($ngx_module_link=ADDON) compilation mode, so we should
# place in static ($ngx_module_link=ADDON) compilation mode, so we must
# explicitly update module "ordering rules".
#
# Default runtime location of ngx_http_modsecurity_module is right before
# ngx_http_chunked_filter_module, but in case if ngx_http_v2_filter_module is
# compiled in, we should put our module before ngx_http_v2_filter_module in
# order to support SecRules processing for HTTP/2.0 requests.
#
if [ "$ngx_module_link" != DYNAMIC ] ; then
pre_module='ngx_http_chunked_filter_module'
if [ "$HTTP_V2" = "YES" ]; then
pre_module='ngx_http_v2_filter_module'
# Reposition modsecurity module to satisfy $modsecurity_dependency
# (this mimics dependency resolution made by ngx_add_module() function
# though less optimal in terms of computational complexity).
modules=
found=
for module in $HTTP_FILTER_MODULES; do
# skip our module name from the original list
if [ "$module" = "$ngx_addon_name" ]; then
continue
fi
if [ -z "${found}" ]; then
for item in $modsecurity_dependency; do
if [ "$module" = "$item" ]; then
modules="${modules} $ngx_addon_name"
found=1
break
fi
done
fi
modules="${modules} $module"
done
if [ -z "${found}" ]; then
# This must never happen since ngx_http_copy_filter_module must be in HTTP_FILTER_MODULES
# and we stated dependency on it in $modsecurity_dependency
echo "$0: error: cannot reposition modsecurity module in HTTP_FILTER_MODULES list"
exit 1
fi
HTTP_FILTER_MODULES=`echo $HTTP_FILTER_MODULES | \
sed -E "s/$ngx_addon_name/ /g" | \
sed -E "s/$pre_module/$pre_module $ngx_addon_name/g"`
HTTP_FILTER_MODULES="${modules}"
fi
55 changes: 25 additions & 30 deletions src/ngx_http_modsecurity_body_filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ ngx_http_modsecurity_body_filter_init(void)
ngx_int_t
ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
int buffer_fully_loadead = 0;
ngx_chain_t *chain = in;
ngx_http_modsecurity_ctx_t *ctx = NULL;
#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
Expand Down Expand Up @@ -135,47 +134,43 @@ ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
}
#endif

int is_request_processed = 0;
for (; chain != NULL; chain = chain->next)
{
/* XXX: chain->buf->last_buf || chain->buf->last_in_chain */
if (chain->buf->last_buf) {
buffer_fully_loadead = 1;
u_char *data = chain->buf->pos;
int ret;

msc_append_response_body(ctx->modsec_transaction, data, chain->buf->last - data);
ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
if (ret > 0) {
return ngx_http_filter_finalize_request(r,
&ngx_http_modsecurity_module, ret);
}
}

if (buffer_fully_loadead == 1)
{
int ret;
ngx_pool_t *old_pool;
/* XXX: chain->buf->last_buf || chain->buf->last_in_chain */
is_request_processed = chain->buf->last_buf;

for (chain = in; chain != NULL; chain = chain->next)
{
u_char *data = chain->buf->start;
if (is_request_processed) {
ngx_pool_t *old_pool;

old_pool = ngx_http_modsecurity_pcre_malloc_init(r->pool);
msc_process_response_body(ctx->modsec_transaction);
ngx_http_modsecurity_pcre_malloc_done(old_pool);

msc_append_response_body(ctx->modsec_transaction, data, chain->buf->end - data);
/* XXX: I don't get how body from modsec being transferred to nginx's buffer. If so - after adjusting of nginx's
XXX: body we can proceed to adjust body size (content-length). see xslt_body_filter() for example */
ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
if (ret > 0) {
return ngx_http_filter_finalize_request(r,
&ngx_http_modsecurity_module, ret);
return ret;
}
}

old_pool = ngx_http_modsecurity_pcre_malloc_init(r->pool);
msc_process_response_body(ctx->modsec_transaction);
ngx_http_modsecurity_pcre_malloc_done(old_pool);
else if (ret < 0) {
return ngx_http_filter_finalize_request(r,
&ngx_http_modsecurity_module, NGX_HTTP_INTERNAL_SERVER_ERROR);

/* XXX: I don't get how body from modsec being transferred to nginx's buffer. If so - after adjusting of nginx's
XXX: body we can proceed to adjust body size (content-length). see xslt_body_filter() for example */
ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
if (ret > 0) {
return ret;
}
else if (ret < 0) {
return ngx_http_filter_finalize_request(r,
&ngx_http_modsecurity_module, NGX_HTTP_INTERNAL_SERVER_ERROR);
}
}
}
else
if (!is_request_processed)
{
dd("buffer was not fully loaded! ctx: %p", ctx);
}
Expand Down
4 changes: 2 additions & 2 deletions src/ngx_http_modsecurity_pre_access.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,10 @@ ngx_http_modsecurity_pre_access_handler(ngx_http_request_t *r)

while (chain && !already_inspected)
{
u_char *data = chain->buf->start;
u_char *data = chain->buf->pos;

msc_append_request_body(ctx->modsec_transaction, data,
chain->buf->last - chain->buf->pos);
chain->buf->last - data);

if (chain->buf->last_buf) {
break;
Expand Down