feat(waf): Request-Body-Inspektion — Coraza sieht jetzt POST/PUT-Payloads — v1.3.6
Bisher inspizierte die WAF nur URL/Querystring + Header (SPOE sendete keinen Body, ProcessRequestBody wurde nie aufgerufen) → blind für POST/PUT-Payloads (Form-SQLi, JSON-Injection, Uploads). Jetzt: - haproxy.cfg.tpl: `option http-buffer-request` im public_https-Frontend, NUR wenn WAF aktiv (.WAFEnabled) — kein RAM-pro-Connection-Overhead sonst. - spoeCfg (haproxy.go): SPOE-Message sendet `body=req.body` an den Agent. - spoe.go: Body einsammeln → tx.WriteRequestBody + tx.ProcessRequestBody nach der Header-Phase (vor MatchedRules-Log, damit Body-Treffer geloggt werden); Interruption blockt in blocking-Mode. Coraza-Engine war schon bereit (SecRequestBodyAccess On + Limits, engine.go). Puffer bis tune.bufsize (~16KB); größere Bodies zur Prüfung gekappt. Render-Test: http-buffer-request nur bei WAF + vor dem SPOE-Filter; body=req.body. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,7 @@ func (a *SPOEAgent) handle(ctx context.Context, w *encoding.ActionWriter, m *enc
|
||||
httpVer string
|
||||
host string
|
||||
rawHdrs string
|
||||
body []byte // gepufferter Request-Body (via HAProxy option http-buffer-request)
|
||||
)
|
||||
|
||||
// Iterate over the key-value pairs HAProxy sent with this message.
|
||||
@@ -63,6 +64,12 @@ func (a *SPOEAgent) handle(ctx context.Context, w *encoding.ActionWriter, m *enc
|
||||
host = string(entry.ValueBytes())
|
||||
case entry.NameEquals("headers"):
|
||||
rawHdrs = string(entry.ValueBytes())
|
||||
case entry.NameEquals("body"):
|
||||
// Kopieren: entry wird nach Reset() wiederverwendet, der
|
||||
// zugrundeliegende Puffer darf nicht referenziert bleiben.
|
||||
if b := entry.ValueBytes(); len(b) > 0 {
|
||||
body = append([]byte(nil), b...)
|
||||
}
|
||||
}
|
||||
entry.Reset()
|
||||
}
|
||||
@@ -119,6 +126,26 @@ func (a *SPOEAgent) handle(ctx context.Context, w *encoding.ActionWriter, m *enc
|
||||
// Evaluate request headers.
|
||||
interruption := tx.ProcessRequestHeaders()
|
||||
|
||||
// Request-Body inspizieren (POST/PUT-Payloads: Form-SQLi, JSON-Injection,
|
||||
// Uploads). Nur wenn die Header-Phase noch nicht geblockt hat. HAProxy
|
||||
// liefert den Body via `option http-buffer-request` (bis tune.bufsize) —
|
||||
// größere Bodies werden zur Prüfung gekappt. Content-Type kam bereits
|
||||
// über die Header, sodass Coraza urlencoded/multipart/json korrekt parst.
|
||||
if interruption == nil && len(body) > 0 {
|
||||
if it, _, err := tx.WriteRequestBody(body); err != nil {
|
||||
slog.Warn("waf: WriteRequestBody", "error", err)
|
||||
} else if it != nil {
|
||||
interruption = it
|
||||
} else {
|
||||
it, err := tx.ProcessRequestBody()
|
||||
if err != nil {
|
||||
slog.Warn("waf: ProcessRequestBody", "error", err)
|
||||
} else if it != nil {
|
||||
interruption = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log all matched rules (detection + blocking).
|
||||
for _, mr := range tx.MatchedRules() {
|
||||
a.sendAlert(host, clientIP, method, uri, mr, interruption != nil)
|
||||
|
||||
Reference in New Issue
Block a user