From 0bf0455c5994eb1558bb937ec0b5a2443fb361e6 Mon Sep 17 00:00:00 2001 From: Matteo Pace Date: Mon, 5 Aug 2024 10:59:36 +0200 Subject: [PATCH] fix: headers leaked during interruptions at phase 3/4 (port) (#166) * fix: headers leaked during interruptions at phase 3/4 (port) * enrich example --- example/Caddyfile | 23 ++++++++++++++--------- interceptor.go | 16 +++++++++++++--- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/example/Caddyfile b/example/Caddyfile index 8ecfc27..913a83f 100644 --- a/example/Caddyfile +++ b/example/Caddyfile @@ -3,8 +3,10 @@ auto_https off order coraza_waf first log { + output stdout format console - level info + level debug + include "http.handlers.waf" } } @@ -15,25 +17,28 @@ Include @coraza.conf-recommended Include @crs-setup.conf.example Include @owasp_crs/*.conf + SecDefaultAction "phase:3,log,auditlog,pass" + SecDefaultAction "phase:4,log,auditlog,pass" + SecDefaultAction "phase:5,log,auditlog,pass" SecRuleEngine On SecDebugLog /dev/stdout SecDebugLogLevel 9 SecRule REQUEST_URI "@streq /admin" "id:101,phase:1,t:lowercase,deny,status:403" SecRule REQUEST_BODY "@rx maliciouspayload" "id:102,phase:2,t:lowercase,deny,status:403" - SecRule RESPONSE_HEADERS::status "@rx 406" "id:103,phase:3,t:lowercase,deny,status:403" + SecRule RESPONSE_STATUS "@rx 406" "id:103,phase:3,t:lowercase,deny,status:403" SecResponseBodyAccess On SecResponseBodyMimeType application/json SecRule RESPONSE_BODY "@contains responsebodycode" "id:104,phase:4,t:lowercase,deny,status:403" ` } - handle_errors 403 { - header X-Blocked "true" - root * /etc/caddy/custom-pages - rewrite * /{err.status_code}.html - file_server - templates - } + handle_errors 403 { + header X-Blocked "true" + root * /etc/caddy/custom-pages + rewrite * /{err.status_code}.html + file_server + templates + } reverse_proxy {$HTTPBIN_HOST:localhost}:8081 } diff --git a/interceptor.go b/interceptor.go index 7f489b0..fb09c5c 100644 --- a/interceptor.go +++ b/interceptor.go @@ -42,7 +42,7 @@ func (i *rwInterceptor) WriteHeader(statusCode int) { i.statusCode = statusCode if it := i.tx.ProcessResponseHeaders(statusCode, i.proto); it != nil { - i.w.Header().Del("Content-Length") + i.cleanHeaders() i.statusCode = obtainStatusCodeFromInterruptionOrDefault(it, i.statusCode) i.flushWriteHeader() return @@ -64,6 +64,13 @@ func (i *rwInterceptor) flushWriteHeader() { } } +// cleanHeaders removes all headers from the response +func (i *rwInterceptor) cleanHeaders() { + for k := range i.w.Header() { + i.w.Header().Del(k) + } +} + // Write buffers the response body until the request body limit is reach or an // interruption is triggered, this buffer is later used to analyse the body in // the response processor. @@ -91,7 +98,9 @@ func (i *rwInterceptor) Write(b []byte) (int, error) { // to it, otherwise we just send it to the response writer. it, n, err := i.tx.WriteResponseBody(b) if it != nil { - i.overrideWriteHeader(it.Status) + // if there is an interruption we must clean the headers and override the status code + i.cleanHeaders() + i.overrideWriteHeader(obtainStatusCodeFromInterruptionOrDefault(it, i.statusCode)) // We only flush the status code after an interruption. i.flushWriteHeader() @@ -164,9 +173,10 @@ func wrap(w http.ResponseWriter, r *http.Request, tx types.Transaction) ( Err: err, } } else if it != nil { + // if there is an interruption we must clean the headers and override the status code + i.cleanHeaders() code := obtainStatusCodeFromInterruptionOrDefault(it, i.statusCode) i.overrideWriteHeader(code) - i.w.Header().Del("Content-Length") i.flushWriteHeader() return caddyhttp.HandlerError{