From 2eef7b2b4207859de43434d5b470987af8f077a2 Mon Sep 17 00:00:00 2001 From: Julian Sommer <115516918+juliansommer@users.noreply.github.com> Date: Wed, 26 Aug 2026 12:17:43 +1000 Subject: [PATCH] fix: use the sigv4 helper to redact session tokens from mismatch logs The local helper masked x-amz-security-token by line prefix, which matched the header form and missed the presigned query form entirely, leaking the token into the exported logs on every signature mismatch. Defer to bluebottle's RedactedCanonicalRequest, which knows the structure and masks both forms. --- spinifex/gateway/auth.go | 16 +--------------- spinifex/gateway/auth_test.go | 2 +- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/spinifex/gateway/auth.go b/spinifex/gateway/auth.go index 326977009..fabb06922 100644 --- a/spinifex/gateway/auth.go +++ b/spinifex/gateway/auth.go @@ -141,7 +141,7 @@ func (gw *GatewayConfig) SigV4AuthMiddleware() func(http.Handler) http.Handler { "accessKeyID", sig.Credential.AccessKeyID, "sourceIP", clientIP, "service", sig.Credential.Service, "action", mismatchAction(r, sig.Credential.Service), - "canonicalRequest", redactedCanonicalRequest(sig), + "canonicalRequest", sig.RedactedCanonicalRequest(), "err", err) // Fingerprinted by the signature as well as the key id: each guess at a // secret produces a different one, while a client retrying an identical @@ -213,20 +213,6 @@ func (gw *GatewayConfig) SigV4AuthMiddleware() func(http.Handler) http.Handler { } } -// redactedCanonicalRequest renders the server's canonical request for a mismatch log with -// the session token masked. The rest is safe: it holds signed header values and a payload -// hash, never the secret key. -func redactedCanonicalRequest(sig *sigv4.SignedRequest) string { - lines := strings.Split(sig.CanonicalRequest(), "\n") - for i, line := range lines { - if strings.HasPrefix(line, "x-amz-security-token:") { - lines[i] = "x-amz-security-token:" - } - } - - return strings.Join(lines, "\n") -} - // signingTime returns the timestamp the client signed with, read straight off the // request. Parse rejects a skewed request before returning a SignedRequest, so the // raw header (or the presigned query arg) is the only copy available to log. diff --git a/spinifex/gateway/auth_test.go b/spinifex/gateway/auth_test.go index 2ef7514fa..dc3f9e6e5 100644 --- a/spinifex/gateway/auth_test.go +++ b/spinifex/gateway/auth_test.go @@ -2214,7 +2214,7 @@ func TestRedactedCanonicalRequest_MasksSessionToken(t *testing.T) { sig, err := sigv4.Parse(req) require.NoError(t, err) - out := redactedCanonicalRequest(sig) + out := sig.RedactedCanonicalRequest() assert.NotContains(t, out, token, "session token must not reach the log") assert.Contains(t, out, "x-amz-security-token:") // The rest must stay intact, or the log is useless for diffing.