diff --git a/.github/nginx/docs/403.html b/.github/nginx/docs/403.html
new file mode 100644
index 0000000..237f5a0
--- /dev/null
+++ b/.github/nginx/docs/403.html
@@ -0,0 +1,10 @@
+
+
+403
+
+
+
+Forbidden 403 - custom error page.
+
+
+
diff --git a/.github/nginx/nginx.conf.redir b/.github/nginx/nginx.conf.redir
new file mode 100644
index 0000000..698951a
--- /dev/null
+++ b/.github/nginx/nginx.conf.redir
@@ -0,0 +1,90 @@
+user www-data;
+worker_processes auto;
+pid /run/nginx.pid;
+worker_cpu_affinity auto;
+
+#working_directory /tmp/cores/;
+worker_rlimit_core 2000M;
+debug_points abort;
+
+#load_module /usr/local/nginx/modules/ngx_http_modsecurity_module.so;
+
+events {
+ worker_connections 768;
+# multi_accept on;
+# use epoll;
+}
+
+worker_rlimit_nofile 33268;
+
+#daemon off;
+#master_process off;
+
+http {
+
+ ##
+ # Basic Settings
+ ##
+
+ types_hash_max_size 2048;
+
+ server_names_hash_bucket_size 64;
+
+ include mime.types;
+ default_type application/octet-stream;
+
+ ##
+ # Logging Settings
+ ##
+
+ #access_log /dev/stdout;
+ #error_log /dev/stdout info;
+ access_log /usr/local/nginx/logs/access.log;
+ error_log /usr/local/nginx/logs/error.log info;
+
+ server_tokens off;
+
+ proxy_hide_header X-Powered-By;
+
+ modsecurity on;
+
+ server {
+ listen 80;
+ server_name modsectest1;
+
+ modsecurity on;
+ modsecurity_rules_file /home/runner/work/ModSecurity-nginx/ModSecurity-nginx/ModSecurity-nginx/.github/nginx/modsecurity.conf;
+ root /usr/local/nginx/html/;
+
+ error_page 403 /403.html;
+
+ location /403.html {
+ internal;
+ }
+
+ location / {
+ try_files $uri /index.html;
+ }
+ }
+
+ server {
+ listen 80;
+ server_name modsectest2;
+
+ modsecurity on;
+ modsecurity_rules_file /home/runner/work/ModSecurity-nginx/ModSecurity-nginx/ModSecurity-nginx/.github/nginx/modsecurity.conf;
+ root /usr/local/nginx/html/;
+
+ error_page 403 /403.html;
+
+ location /403.html {
+ internal;
+ }
+
+ location / {
+ try_files $uri /index.html;
+ }
+ }
+
+}
+
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index dc1bc1b..9841dd1 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -140,6 +140,78 @@ jobs:
echo "FAIL"
exit 1
fi
+ - name: Start Nginx with redir
+ run: |
+ sudo killall nginx
+ sudo /usr/local/nginx/sbin/nginx -c /home/runner/work/ModSecurity-nginx/ModSecurity-nginx/ModSecurity-nginx/.github/nginx/nginx.conf.redir
+ - name: Run attack test vhost 1
+ run: |
+ status=$(curl -sSo /dev/null -w %{http_code} -I -X GET -H "Host: modsectest1" "http://localhost/?q=attack")
+ if [ "${status}" == "403" ]; then
+ echo "OK"
+ else
+ echo "FAIL"
+ exit 1
+ fi
+ - name: Run non-attack test vhost 1 (redir config)
+ run: |
+ status=$(curl -sSo /dev/null -w %{http_code} -I -X GET -H "Host: modsectest1" "http://localhost/?q=1")
+ if [ "${status}" == "200" ]; then
+ echo "OK"
+ else
+ echo "FAIL"
+ exit 1
+ fi
+ - name: Run attack test vhost 2 (redir config)
+ run: |
+ status=$(curl -sSo /dev/null -w %{http_code} -I -X GET -H "Host: modsectest2" "http://localhost/?q=attack")
+ if [ "${status}" == "403" ]; then
+ echo "OK"
+ else
+ echo "FAIL"
+ exit 1
+ fi
+ - name: Run non-attack test vhost 2 (redir config)
+ run: |
+ status=$(curl -sSo /dev/null -w %{http_code} -I -X GET -H "Host: modsectest2" "http://localhost/?q=1")
+ if [ "${status}" == "200" ]; then
+ echo "OK"
+ else
+ echo "FAIL"
+ exit 1
+ fi
+ - name: Run file consistency check 1 (redir config)
+ run: |
+ curl -sS "http://localhost/data50k.json" --output data50k.json
+ if [ -f data50k.json ]; then
+ diff data50k.json /usr/local/nginx/html/data50k.json > /dev/null
+ if [ $? -eq 0 ]; then
+ ls -l data50k.json /usr/local/nginx/html/data50k.json
+ echo "OK"
+ else
+ echo "FAIL"
+ exit 2
+ fi
+ else
+ echo "FAIL"
+ exit 1
+ fi
+ - name: Run file consistency check 2 (redir config)
+ run: |
+ curl -sS "http://localhost/plugged.png" --output plugged.png
+ if [ -f plugged.png ]; then
+ diff plugged.png /usr/local/nginx/html/plugged.png > /dev/null
+ if [ $? -eq 0 ]; then
+ ls -l plugged.png /usr/local/nginx/html/plugged.png
+ echo "OK"
+ else
+ echo "FAIL"
+ exit 2
+ fi
+ else
+ echo "FAIL"
+ exit 1
+ fi
build-windows:
diff --git a/src/ngx_http_modsecurity_body_filter.c b/src/ngx_http_modsecurity_body_filter.c
index 86bccc7..0c28e3c 100644
--- a/src/ngx_http_modsecurity_body_filter.c
+++ b/src/ngx_http_modsecurity_body_filter.c
@@ -50,7 +50,7 @@ ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
return ngx_http_next_body_filter(r, in);
}
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
dd("body filter, recovering ctx: %p", ctx);
diff --git a/src/ngx_http_modsecurity_common.h b/src/ngx_http_modsecurity_common.h
index 11fdc2d..cde48a5 100644
--- a/src/ngx_http_modsecurity_common.h
+++ b/src/ngx_http_modsecurity_common.h
@@ -99,6 +99,7 @@ typedef struct {
unsigned processed:1;
unsigned logged:1;
unsigned intervention_triggered:1;
+ unsigned request_body_processed:1;
} ngx_http_modsecurity_ctx_t;
@@ -139,6 +140,7 @@ extern ngx_module_t ngx_http_modsecurity_module;
/* ngx_http_modsecurity_module.c */
int ngx_http_modsecurity_process_intervention (Transaction *transaction, ngx_http_request_t *r, ngx_int_t early_log);
ngx_http_modsecurity_ctx_t *ngx_http_modsecurity_create_ctx(ngx_http_request_t *r);
+ngx_http_modsecurity_ctx_t *ngx_http_modsecurity_get_module_ctx(ngx_http_request_t *r);
char *ngx_str_to_char(ngx_str_t a, ngx_pool_t *p);
#if (NGX_PCRE2)
#define ngx_http_modsecurity_pcre_malloc_init(x) NULL
diff --git a/src/ngx_http_modsecurity_header_filter.c b/src/ngx_http_modsecurity_header_filter.c
index 257e7fd..03b8764 100644
--- a/src/ngx_http_modsecurity_header_filter.c
+++ b/src/ngx_http_modsecurity_header_filter.c
@@ -109,7 +109,7 @@ ngx_http_modsecurity_store_ctx_header(ngx_http_request_t *r, ngx_str_t *name, ng
ngx_http_modsecurity_conf_t *mcf;
ngx_http_modsecurity_header_t *hdr;
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
if (ctx == NULL || ctx->sanity_headers_out == NULL) {
return NGX_ERROR;
}
@@ -152,7 +152,7 @@ ngx_http_modsecurity_resolv_header_server(ngx_http_request_t *r, ngx_str_t name,
ngx_str_t value;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
if (r->headers_out.server == NULL) {
if (clcf->server_tokens) {
@@ -186,7 +186,7 @@ ngx_http_modsecurity_resolv_header_date(ngx_http_request_t *r, ngx_str_t name, o
ngx_http_modsecurity_ctx_t *ctx = NULL;
ngx_str_t date;
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
if (r->headers_out.date == NULL) {
date.data = ngx_cached_http_time.data;
@@ -216,7 +216,7 @@ ngx_http_modsecurity_resolv_header_content_length(ngx_http_request_t *r, ngx_str
ngx_str_t value;
char buf[NGX_INT64_LEN+2];
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
if (r->headers_out.content_length_n > 0)
{
@@ -243,7 +243,7 @@ ngx_http_modsecurity_resolv_header_content_type(ngx_http_request_t *r, ngx_str_t
{
ngx_http_modsecurity_ctx_t *ctx = NULL;
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
if (r->headers_out.content_type.len > 0)
{
@@ -270,7 +270,7 @@ ngx_http_modsecurity_resolv_header_last_modified(ngx_http_request_t *r, ngx_str_
u_char buf[1024], *p;
ngx_str_t value;
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
if (r->headers_out.last_modified_time == -1) {
return 1;
@@ -302,7 +302,7 @@ ngx_http_modsecurity_resolv_header_connection(ngx_http_request_t *r, ngx_str_t n
ngx_str_t value;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
connection = "upgrade";
@@ -353,7 +353,7 @@ ngx_http_modsecurity_resolv_header_transfer_encoding(ngx_http_request_t *r, ngx_
if (r->chunked) {
ngx_str_t value = ngx_string("chunked");
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
ngx_http_modsecurity_store_ctx_header(r, &name, &value);
@@ -380,7 +380,7 @@ ngx_http_modsecurity_resolv_header_vary(ngx_http_request_t *r, ngx_str_t name, o
if (r->gzip_vary && clcf->gzip_vary) {
ngx_str_t value = ngx_string("Accept-Encoding");
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
ngx_http_modsecurity_store_ctx_header(r, &name, &value);
@@ -422,7 +422,7 @@ ngx_http_modsecurity_header_filter(ngx_http_request_t *r)
/* XXX: if NOT_MODIFIED, do we need to process it at all? see xslt_header_filter() */
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
dd("header filter, recovering ctx: %p", ctx);
diff --git a/src/ngx_http_modsecurity_log.c b/src/ngx_http_modsecurity_log.c
index 167b2d3..094685c 100644
--- a/src/ngx_http_modsecurity_log.c
+++ b/src/ngx_http_modsecurity_log.c
@@ -41,17 +41,9 @@ ngx_http_modsecurity_log_handler(ngx_http_request_t *r)
{
ngx_pool_t *old_pool;
ngx_http_modsecurity_ctx_t *ctx;
- ngx_http_modsecurity_conf_t *mcf;
dd("catching a new _log_ phase handler");
- mcf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity_module);
- if (mcf == NULL || mcf->enable != 1)
- {
- dd("ModSecurity not enabled... returning");
- return NGX_OK;
- }
-
/*
if (r->method != NGX_HTTP_GET &&
r->method != NGX_HTTP_POST && r->method != NGX_HTTP_HEAD) {
@@ -60,13 +52,13 @@ ngx_http_modsecurity_log_handler(ngx_http_request_t *r)
return NGX_OK;
}
*/
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
dd("recovering ctx: %p", ctx);
if (ctx == NULL) {
- dd("something really bad happened here. returning NGX_ERROR");
- return NGX_ERROR;
+ dd("ModSecurity not enabled or error occurred");
+ return NGX_OK;
}
if (ctx->logged) {
diff --git a/src/ngx_http_modsecurity_module.c b/src/ngx_http_modsecurity_module.c
index c90b3f6..e8a5f4b 100644
--- a/src/ngx_http_modsecurity_module.c
+++ b/src/ngx_http_modsecurity_module.c
@@ -149,7 +149,7 @@ ngx_http_modsecurity_process_intervention (Transaction *transaction, ngx_http_re
dd("processing intervention");
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
if (ctx == NULL)
{
return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -313,6 +313,27 @@ ngx_http_modsecurity_create_ctx(ngx_http_request_t *r)
return ctx;
}
+ngx_inline ngx_http_modsecurity_ctx_t *
+ngx_http_modsecurity_get_module_ctx(ngx_http_request_t *r)
+{
+ ngx_http_modsecurity_ctx_t *ctx;
+ ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ if (ctx == NULL) {
+ /*
+ * refer /src/http/modules/ngx_http_realip_module.c
+ * if module context was reset, the original address
+ * can still be found in the cleanup handler
+ */
+ ngx_pool_cleanup_t *cln;
+ for (cln = r->pool->cleanup; cln; cln = cln->next) {
+ if (cln->handler == ngx_http_modsecurity_cleanup) {
+ ctx = cln->data;
+ break;
+ }
+ }
+ }
+ return ctx;
+}
char *
ngx_conf_set_rules(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
diff --git a/src/ngx_http_modsecurity_pre_access.c b/src/ngx_http_modsecurity_pre_access.c
index ea5c021..75ac45d 100644
--- a/src/ngx_http_modsecurity_pre_access.c
+++ b/src/ngx_http_modsecurity_pre_access.c
@@ -27,7 +27,7 @@ ngx_http_modsecurity_request_read(ngx_http_request_t *r)
{
ngx_http_modsecurity_ctx_t *ctx;
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
#if defined(nginx_version) && nginx_version >= 8011
r->main->count--;
@@ -70,7 +70,7 @@ ngx_http_modsecurity_pre_access_handler(ngx_http_request_t *r)
}
*/
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
dd("recovering ctx: %p", ctx);
@@ -80,6 +80,11 @@ ngx_http_modsecurity_pre_access_handler(ngx_http_request_t *r)
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
+ if (ctx->request_body_processed) {
+ // should we use r->internal or r->filter_finalize?
+ return NGX_DECLINED;
+ }
+
if (ctx->intervention_triggered) {
return NGX_DECLINED;
}
@@ -212,6 +217,7 @@ ngx_http_modsecurity_pre_access_handler(ngx_http_request_t *r)
old_pool = ngx_http_modsecurity_pcre_malloc_init(r->pool);
msc_process_request_body(ctx->modsec_transaction);
+ ctx->request_body_processed = 1;
ngx_http_modsecurity_pcre_malloc_done(old_pool);
ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r, 0);
diff --git a/src/ngx_http_modsecurity_rewrite.c b/src/ngx_http_modsecurity_rewrite.c
index 926cf70..eaff1cc 100644
--- a/src/ngx_http_modsecurity_rewrite.c
+++ b/src/ngx_http_modsecurity_rewrite.c
@@ -46,7 +46,7 @@ ngx_http_modsecurity_rewrite_handler(ngx_http_request_t *r)
dd("catching a new _rewrite_ phase handler");
- ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+ ctx = ngx_http_modsecurity_get_module_ctx(r);
dd("recovering ctx: %p", ctx);