GHSA-gv83-gqw6-9j2c GoFiber never set HSTS header in helmet middleware due to incorrect protocol check
SummaryThe `helmet` middleware in gofiber/fiber never sets the `Strict-Transport-Security` (HSTS) response header, even when `HSTSMaxAge` is explicitly configured, because the condition check at `helmet.go:67` uses `c.Protocol()` — which returns the HTTP protocol version string (e.g., `"HTTP/1.1"`, `"HTTP/2.0"`) — instead of `c.Scheme()` — which returns the URL scheme (`"http"` or `"https"`). Since `c.Protocol()` never equals `"https"` in any real deployment, the HSTS header is permanently disabled, defeating the security protection.Details**Root cause:** `middleware/helmet/helmet.go`, line 67:`c.Protocol()` (defined at `req.go:865-867`) delegates to `fasthttp.Request.Header.Protocol()`, which returns the HTTP protocol version:`"HTTP/1.1"` for HTTP/1.1 connections`"HTTP/2.0"` for HTTP/2 connectionsThe correct method is `c.Scheme()` (defined at `req.go:844-862`), which returns:`"http"` for plain HTTP connections`"https"` for TLS connectionsSince `"HTTP/1.1" != "https"` always evaluates to `true`, the entire HSTS block (lines 67-76) is dead code.**Note on test coverage:** The existing helmet test (`helmet_test.go`) passes because it uses `ctx.Request.Header.SetProtocol("https")` to artificially force `Protocol()` to return `"https"`. However, `fasthttp.Request.Header.SetProtocol()` sets the HTTP version field, and real HTTP requests never have protocol `"https"` — they have `"HTTP/1.1"` or `"HTTP/2.0"`. The test is validating the wrong thing.PoC**Clean-checkout maintainer-runnable recipe:**Save the following as `middleware/helmet/poc_hsts_test.go`:Run: `go test -run Test_PoC_HSTS_NeverSet -v ./middleware/helmet/`**Expected vulnerable output:****Expected output after fix:****Observed output from this environment (commit `ee98695f`):****Negative/control case:** With `HSTSMaxAge: 0` (default), HSTS is correctly not set (this is expected behavior, not a bug).**Cleanup:** Remove `poc_hsts_test.go` after verification.ImpactThe HSTS header is never applied in production, leaving all users vulnerable to:**SSL stripping attacks:** An active network attacker can downgrade HTTPS connections to HTTP, intercepting traffic between the client and server.**Protocol downgrade:** Without HSTS, browsers will silently accept HTTP connections to the site, even if the site supports HTTPS.**Cookie theft over HTTP:** Session cookies without the `Secure` flag will be sent over HTTP if the user is tricked into an HTTP connection.This affects any application that:Uses the `helmet` middlewareConfigures `HSTSMaxAge > 0` expecting HSTS protectionServes traffic over HTTPSThe vulnerability requires an active MITM attacker on the network path, which is realistic in public Wi-Fi, corporate networks, and ISP-level scenarios.Suggested remediationIn `middleware/helmet/helmet.go`, line 67, replace `c.Protocol()` with `c.Scheme()`:Additionally, update the existing test to use a realistic TLS simulation instead of `SetProtocol("https")`:**Regression test:** Add a test case that verifies HSTS is set when `req.TLS` is non-nil and `HSTSMaxAge > 0`, without using `SetProtocol`.
CVSS v3.1 score: 4.8