- Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Describe the bug
When response code is changed during phase 4 and the original response code is other than 200, Apache httpd does not execute ErrorDocument directive and bad response is generated.
Logs and dumps
Output of:
- DebugLogs (level 9)
- AuditLogs
- Error logs
To Reproduce
- Add following configuration:
ProxyPass /helloworld http://localhost:9090/unavaible ErrorDocument 403 /custompage.html SecRule RESPONSE_PROTOCOL "@contains HTTP" "id:'4',phase:4,auditlog,log,deny,status:403,msg:'yay!'"
- Create and place custompage.html page to server root.
- execute
$ curl -v localhost/helloworld
Actual behavior
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>403 Forbidden</title> </head><body> <h1>Forbidden</h1> <p>You don't have permission to access this resource.</p> <p>Additionally, a 503 Service Unavailable error was encountered while trying to use an ErrorDocument to handle the request.</p> </body></html>
Expected behavior
Proper rewritten response with custom error document:
Server (please complete the following information):
- ModSecurity version (and connector): mod_security-2.9.6
- WebServer: httpd-2.4.37
- OS (and distro): Linux Fedora
Rule Set (please complete the following information):
- Running any public or commercial rule set? No
Additional context
In my opinion, this is caused by apache httpds assumption that changed status of request which got through filter is a recursive error which occured during handling of previous errors.
See ap_die_r
function in http module of apache, particularly this part of code:
/* * The following takes care of Apache redirects to custom response URLs * Note that if we are already dealing with the response to some other * error condition, we just report on the original error, and give up on * any attempt to handle the other thing "intelligently"... */ if (recursive_error != HTTP_OK) { while (r_1st_err->prev && (r_1st_err->prev->status != HTTP_OK)) r_1st_err = r_1st_err->prev; /* Get back to original error */ if (r_1st_err != r) { /* The recursive error was caused by an ErrorDocument specifying * an internal redirect to a bad URI. ap_internal_redirect has * changed the filter chains to point to the ErrorDocument's * request_rec. Back out those changes so we can safely use the * original failing request_rec to send the canned error message. * * ap_send_error_response gets rid of existing resource filters * on the output side, so we can skip those. */ update_r_in_filters(r_1st_err->proto_output_filters, r, r_1st_err); update_r_in_filters(r_1st_err->input_filters, r, r_1st_err); } custom_response = NULL; /* Do NOT retry the custom thing! */ } else { int error_index = ap_index_of_response(type); custom_response = ap_response_code_string(r, error_index); recursive_error = 0; }
Clearing the original response code with f->r->status = 200;
in send_error_bucket
function appears to fix this issue.
This issue is a continuation of #533 and extends findings of Marc Stern in #533 (comment)