Use After Free in Fiber with Jwt Tokens
Use After Free in Fiber with Jwt Tokens — how this specific combination creates or exposes the vulnerability
Use After Free occurs when memory is accessed after it has been deallocated. In the context of a Fiber-based API that uses JWT tokens for authentication, the risk arises when token lifecycle handling interacts with asynchronous or pooled execution contexts in ways that retain references to buffers that have been released. Fiber is a high-performance HTTP framework for Go that uses a per-request stack allocated on a transient execution context; when request-scoped objects such as parsed JWT tokens or their backing byte slices are referenced after the request context has advanced or been recycled, the memory may be reused for another request.
Consider a pattern where a JWT token is parsed into a struct and a pointer to that struct is stored (for example, in a context value or a request-scoped cache) while the token’s underlying byte slice points into a request buffer managed by Fiber’s request pool. If the request handler schedules work on a goroutine that outlives the request handler’s return, and the buffer backing the token is returned to the pool and reused for a subsequent request, the scheduled work may read or write credentials or claims from the reclaimed buffer. This is a classic Use After Free: the token object remains reachable, but the backing memory has been repurposed, potentially exposing prior request data or enabling attacker-controlled data to be interpreted as claims.
In practice, this can surface when middleware caches parsed token pointers for performance, or when claims are copied into structures that are stored beyond the request lifetime without deep copying the underlying data. The vulnerability is not in JWT parsing itself, but in how the runtime manages lifetimes of buffers that back token bytes and how those bytes are referenced across asynchronous boundaries. An attacker who can influence token values or timing may be able to observe or manipulate data read from the freed memory, leading to information disclosure or logic bypass.
middleBrick detects scenarios where token-related findings intersect with asynchronous handling patterns by correlating runtime behavior with spec-driven execution paths. For API scanning, this means flagging endpoints that accept JWT tokens and exhibit characteristics such as missing input validation on token claims, missing rate limiting that could enable rapid token submission to probe timing issues, and missing encryption in transit, while noting that the scanner reports findings and provides remediation guidance rather than attempting to patch the runtime behavior.
Using concrete JWT code in Fiber helps illustrate safe patterns. Below is an example of parsing a token without retaining references to request-scoped buffers beyond the request lifetime:
// Safe: token bytes are copied into a new string; no pointer to request buffer is kept
func ParseTokenSafe(c *fiber.Ctx) error {
auth := c.Get("Authorization")
if auth == "" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// Assume "Bearer <token>" format
parts := strings.Split(auth, " ")
if len(parts) != 2 || parts[0] != "Bearer" {
return c.SendStatus(fiber.StatusBadRequest)
}
raw := parts[1]
// Explicit copy: convert to new string, avoiding any shared backing array
tokenString := string([]byte(raw))
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if err != nil || !token.Valid {
return c.SendStatus(fiber.StatusUnauthorized)
}
// Use claims here within request scope only
if claims, ok := token.Claims.(jwt.MapClaims); ok {
userID, _ := claims["sub"].(string)
_ = userID // process within request
}
return c.SendStatus(fiber.StatusOK)
}
Contrast this with an unsafe pattern where a pointer to token bytes derived from the request buffer is stored:
// Unsafe: keeping a pointer to c.Body() or token bytes can lead to Use After Free
var cachedToken unsafe.Pointer
func UnsafeCache(c *fiber.Ctx) error {
auth := c.Get("Authorization")
parts := strings.Split(auth, " ")
if len(parts) != 2 {
return c.SendStatus(fiber.StatusBadRequest)
}
b := []byte(parts[1]) // points to request buffer in some implementations
cachedToken = unsafe.Pointer(&b) // DO NOT DO THIS
// request returns, buffer may be reused
return c.SendStatus(fiber.StatusOK)
}
To mitigate Use After Free with JWT tokens in Fiber, ensure token bytes are deep-copied, avoid storing pointers to request-scoped buffers, and do not retain context values that reference token backing memory across asynchronous or pooled executions. Combine these practices with strong input validation, rate limiting, and transport encryption to reduce the attack surface.
Jwt Tokens-Specific Remediation in Fiber — concrete code fixes
Remediation focuses on ensuring JWT token handling does not keep references to mutable or pooled buffers and that token lifecycle is confined to the request scope. The safest approach is to treat token bytes as immutable values and to copy them before any storage or cross-goroutine sharing.
Use standard library functions to copy bytes into new strings or slices. For example, when extracting the token from the Authorization header, allocate a new string:
// Secure extraction and copy
func GetTokenCopy(c *fiber.Ctx) string {
auth := c.Get("Authorization")
if auth == "" {
return ""
}
parts := strings.Split(auth, " ")
if len(parts) != 2 || parts[0] != "Bearer" {
return ""
}
// Create a new string from a copy of the bytes
return string(memory.Copy(make([]byte, len(parts[1])), memclr.NoClear, parts[1]))
}
When parsing the token, prefer passing a string rather than a byte slice that may alias request buffers, and ensure the parser does not retain references to external memory. Configure the JWT parser with appropriate methods and keys scoped to the handler or service lifetime, not request-scoped caches:
// Parse and validate within request scope; no external caching of parsed token
func ValidateTokenInRequest(c *fiber.Ctx) error {
tokenStr := GetTokenCopy(c)
if tokenStr == "" {
return c.Status(fiber.StatusUnauthorized).SendString("missing token")
}
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
// Validate signing method and key lookup here
return jwtKey, nil
})
if err != nil {
return c.Status(fiber.StatusUnauthorized).SendString("invalid token")
}
if !token.Valid {
return c.Status(fiber.StatusUnauthorized).SendString("token invalid")
}
// Work with claims locally
if claims, ok := token.Claims.(jwt.MapClaims); ok {
subject, ok := claims["sub"].(string)
if !ok {
return c.Status(fiber.StatusBadRequest).SendString("invalid claims")
}
// Use subject within request handling
_ = subject
}
return c.SendStatus(fiber.StatusOK)
}
Avoid storing parsed *jwt.Token or its claims in global or request-context values that may outlive the request. If you must cache public key material or configuration, store only immutable data such as the key itself or verified key identifiers (kid), and re-parse the token on each request using the fresh token string copy.
For asynchronous workloads, copy all required claim values into new structures before spawning goroutines:
// Safe cross-goroutine usage: copy claims into a new struct
type SessionData struct {
UserID string
Exp int64
}
func HandleAsync(c *fiber.Ctx) error {
tokenStr := GetTokenCopy(c)
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) { return jwtKey, nil })
if err != nil || !token.Valid {
return c.SendStatus(fiber.StatusUnauthorized)
}
if claims, ok := token.Claims.(jwt.MapClaims); ok {
sd := SessionData{
UserID: claims["sub"].(string),
Exp: int64(claims["exp"].(float64)),
}
// Pass sd to async worker; it owns its own copy
go func(data SessionData) {
// use data safely
_ = data
}(sd)
}
return c.SendStatus(fiber.StatusOK)
}
Additionally, enforce transport encryption and validate token audiences and issuers to reduce the impact of any residual memory safety issues. Combine these coding practices with runtime scanning using middleBrick to identify insecure token handling patterns and receive prioritized remediation guidance mapped to frameworks such as OWASP API Top 10.