PT-2026-56054 · Go · Github.Com/Gofiber/Fiber

Published

2026-07-06

·

Updated

2026-07-06

·

CVE-2026-53624

CVSS v3.1

4.8

Medium

VectorAV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N

Summary

The 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:
go
if c.Protocol() == "https" && cfg.HSTSMaxAge != 0 {
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 connections
The correct method is c.Scheme() (defined at req.go:844-862), which returns:
  • "http" for plain HTTP connections
  • "https" for TLS connections
Since "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:
  1. Save the following as middleware/helmet/poc hsts test.go:
go
package helmet

import (
  "crypto/tls"
  "net/http/httptest"
  "testing"

  "github.com/gofiber/fiber/v3"
)

func Test PoC HSTS NeverSet(t *testing.T) {
  app := fiber.New()
  app.Use(New(Config{
    HSTSMaxAge: 31536000,
  }))
  app.Get("/", func(c fiber.Ctx) error {
    return c.SendString("ok")
  })

  // Simulate HTTPS connection
  req := httptest.NewRequest(fiber.MethodGet, "/", nil)
  req.TLS = &tls.ConnectionState{}

  resp,  := app.Test(req)
  hsts := resp.Header.Get("Strict-Transport-Security")

  if hsts == "" {
    t.Log("BUG CONFIRMED: HSTS header not set. c.Protocol() returns 'HTTP/1.1', not 'https'")
    t.Log("Fix: change c.Protocol() == 'https' to c.Scheme() == 'https' on line 67")
  }
}
  1. Run: go test -run Test PoC HSTS NeverSet -v ./middleware/helmet/
Expected vulnerable output:
=== RUN  Test PoC HSTS NeverSet
  BUG CONFIRMED: HSTS header not set. c.Protocol() returns 'HTTP/1.1', not 'https'
  Fix: change c.Protocol() == 'https' to c.Scheme() == 'https' on line 67
--- PASS: Test PoC HSTS NeverSet
Expected output after fix:
=== RUN  Test PoC HSTS NeverSet
--- PASS: Test PoC HSTS NeverSet
  (HSTS header is set: "max-age=31536000; includeSubDomains")
Observed output from this environment (commit ee98695f):
=== RUN  Test PoC HSTS NeverSet
  poc hsts test.go:39: HSTS header value: ""
  poc hsts test.go:42: BUG CONFIRMED: HSTS header is NOT set even over TLS
  poc hsts test.go:43: Root cause: helmet.go:67 uses c.Protocol() which returns HTTP version
  poc hsts test.go:44: c.Protocol() returns 'HTTP/1.1' not 'https'
  poc hsts test.go:45: Fix: use c.Scheme() == 'https' instead of c.Protocol() == 'https'
--- PASS: Test PoC HSTS NeverSet
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.

Impact

The 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:
  1. Uses the helmet middleware
  2. Configures HSTSMaxAge > 0 expecting HSTS protection
  3. Serves traffic over HTTPS
The vulnerability requires an active MITM attacker on the network path, which is realistic in public Wi-Fi, corporate networks, and ISP-level scenarios.

Suggested remediation

In middleware/helmet/helmet.go, line 67, replace c.Protocol() with c.Scheme():
go
// Before (broken):
if c.Protocol() == "https" && cfg.HSTSMaxAge != 0 {

// After (fixed):
if c.Scheme() == "https" && cfg.HSTSMaxAge != 0 {
Additionally, update the existing test to use a realistic TLS simulation instead of SetProtocol("https"):
go
// Before (artificial - sets HTTP version to "https" which never happens in practice):
ctx.Request.Header.SetProtocol("https")

// After (realistic - simulates TLS connection):
ctx.RequestCtx().Request.Header.SetProtocol("HTTP/1.1")
ctx.RequestCtx().TLS = &tls.ConnectionState{}
Regression test: Add a test case that verifies HSTS is set when req.TLS is non-nil and HSTSMaxAge > 0, without using SetProtocol.

Fix

Cleartext Transmission of Sensitive Information

Found an issue in the description? Have something to add? Feel free to write us 👾

Weakness Enumeration

Related Identifiers

CVE-2026-53624
GHSA-GV83-GQW6-9J2C

Affected Products

Github.Com/Gofiber/Fiber