Session Fixation in Fiber with Jwt Tokens
Session Fixation in Fiber with Jwt Tokens — how this specific combination creates or exposes the vulnerability
Session fixation is a class of vulnerability where an attacker sets or guesses a user’s session identifier and then tricks the user into authenticating with that known value. While traditional session fixation often targets server-side session stores and cookies, using JWT tokens in Fiber can still introduce fixation-like risks when token issuance does not rotate or bind the token to a post-authentication context. In such setups, an attacker who can influence the initial token value (for example, by registering a predictable identifier or by provoking an issuance flow before login) may later impersonate the victim once the victim logs in and accepts the attacker-provided token.
With JWT tokens in Fiber, consider a scenario where an endpoint issues a signed token before authentication (e.g., a pre‑auth “session” token) and a second endpoint issues a new token after successful login without invalidating the prior token or ensuring a strict one‑to‑one mapping between user identity and token. If the token contains a user identifier claim that is set before login (e.g., username or a public user ID supplied by the client), an attacker can craft a link that includes that identifier. When the victim logs in, the server may issue a token that still reflects the attacker‑chosen identifier, enabling the attacker to hijack the authenticated session by presenting the token.
Another relevant risk arises when tokens are accepted without proper validation of binding context, such as IP or user‑agent. In Fiber, if a JWT token issued after login is accepted on subsequent requests without verifying that critical context has changed, an attacker who obtained a token before login might reuse it after the victim authenticates—especially if token revocation or rotation is not enforced. Because JWT tokens are typically self‑contained and may carry claims like roles or permissions, a fixed or predictable token issued before authentication can grant the attacker elevated access once the victim completes login.
The risk is compounded when token generation logic does not differentiate between pre‑auth and post‑auth issuance flows. For example, if the same signing key and claims template are used for both flows, and there is no check that the authenticated user matches the token subject at issuance, an attacker can force a token issuance with a chosen subject and later use that token after the victim authenticates. MiddleBrick’s LLM/AI Security checks highlight the importance of ensuring that token issuance after authentication does not simply reuse or confirm an attacker‑supplied identifier, and that claims are tightly bound to the authenticated identity.
Jwt Tokens-Specific Remediation in Fiber — concrete code fixes
Remediation centers on ensuring that token issuance after authentication is independent of any pre‑auth token, that claims are derived from verified authentication state, and that tokens are rotated or invalidated on login. Below are concrete, idiomatic examples for Fiber using the standard JWT middleware approach.
1. Do not issue tokens before authentication
Avoid issuing any JWT token until credentials are verified. If you need a provisional identifier for anonymous interactions, use a server‑side reference that cannot be used to derive post‑login tokens.
2. Issue a fresh token after successful login with strict claims
// fiber-jwt-login.go
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/jwt"
"github.com/golang-jwt/jwt/v5"
"time"
)
func main() {
app := fiber.New()
jwtMiddleware := jwt.New(jwt.Config{
SigningKey: jwt.SigningKey{Key: []byte("your_strong_secret")},
})
// Public login endpoint
app.Post("/login", func(c *fiber.Ctx) error {
var creds struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := c.BodyParser(&creds); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid request"})
}
// TODO: validate credentials against your user store
if creds.Username != "alice" || creds.Password != "correcthorse" {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "invalid credentials"})
}
// Create a fresh token bound to the authenticated user
claims := jwt.MapClaims{
"sub": creds.Username,
"iat": time.Now().Unix(),
"exp": time.Now().Add(time.Hour * 24).Unix(),
"roles": []string{"user"},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
signedToken, err := token.SignedString(jwtMiddleware.Config.SigningKey)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to generate token"})
}
// Set token in Authorization header or secure httpOnly cookie as appropriate
return c.JSON(fiber.Map{"token": signedToken})
})
// Protected route example
app.Get("/profile", jwtMiddleware.Handler, func(c *fiber.Ctx) error {
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
return c.JSON(fiber.Map{"username": claims["sub"], "roles": claims["roles"]})
})
app.Listen(":3000")
}
3. Rotate tokens and avoid reusing pre‑auth identifiers
Ensure that post‑login token generation does not accept a subject from client‑supplied claims. Instead, derive the subject from verified user data (e.g., database ID or verified username) and sign a new token. Invalidate or ignore any token issued before authentication when a user logs in.
4. Validate token binding context where applicable
Consider adding additional claims such as jti (JWT ID) and validate on each request if you implement token revocation. While Fiber does not enforce binding by default, you can implement middleware checks to compare token metadata with session state to detect reuse anomalies.
5. Example of insecure pattern to avoid
// INSECURE: using client-supplied username in token before verifying credentials
app.Post("/login-insecure", func(c *fiber.Ctx) error {
var req struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid request"})
}
// Do NOT issue a token before validating credentials
// DO NOT echo req.Username into claims before verification
// Instead, verify first, then issue a clean token as shown above
})