Session Fixation in Fiber with Hmac Signatures
Session Fixation in Fiber with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Session fixation occurs when an application forces a user to use a known session identifier, allowing an attacker to hijack the session after authentication. In the Go web framework Fiber, this risk can be amplified when Hmac Signatures are used for session integrity but session identifiers are not properly randomized or bound to the Hmac context.
Consider a typical setup where a server stores a session ID in a cookie and then signs request data (e.g., path, method, and a user claim) using an Hmac key. If the session ID is predictable or reused across authentication boundaries, an attacker who can set a victim’s cookie value can force the victim to authenticate under a session identifier the attacker knows. After the victim logs in, the attacker can replay the signed requests using the same session ID and Hmac key derivation path, bypassing authenticity checks because the signature covers the session ID and attacker-chosen data.
For example, imagine a login flow that does not rotate the session identifier upon authentication. The client sends a cookie session=attacker_chosen_value, and the server responds with an Hmac-signed payload like {session: "attacker_chosen_value", user: "victim", action: "login"}. The signature is valid because the server’s Hmac key signs the exact bytes. Post-login, the server may continue to trust the same session identifier and signature scheme, allowing the attacker to reuse the signed payload to perform actions as the victim, since the signature does not protect against a fixed session context being chosen by the attacker.
Additionally, if the Hmac signature incorporates only partial request metadata (e.g., method and path) and the session ID is supplied separately but implicitly trusted, an attacker can fix the session ID before authentication and then craft requests that appear properly signed. The vulnerability here is not in the Hmac algorithm itself, which remains cryptographically sound, but in the session lifecycle management: failing to issue a new, server-side bound session identifier after authentication and failing to include the session identifier inside the signed payload in a way that ties it to the authenticated user.
To detect such issues, scanners like middleBrick analyze the unauthenticated attack surface and check whether session identifiers are rotated after login and whether they are covered by Hmac signatures. This helps identify scenarios where an attacker can force a known session context and leverage Hmac-signed requests without detection.
Hmac Signatures-Specific Remediation in Fiber — concrete code fixes
Remediation focuses on ensuring session identifiers are unpredictable, bound to the authenticated user, and included in the Hmac signature scope. Always generate a new session identifier upon successful authentication and include it as part of the signed data structure.
Example: Secure login flow in Fiber with Hmac Signatures.
// generateSessionID produces a cryptographically random session token
func generateSessionID() (string, error) {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(b), nil
}
// computeHmac returns hex-encoded Hmac for payload using a server-side key
func computeHmac(payload string, key []byte) string {
mac := hmac.New(sha256.New, key)
mac.Write([]byte(payload))
return hex.EncodeToString(mac.Sum(nil))
}
// LoginHandler authenticates a user and rotates the session identifier
func LoginHandler(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"})
}
// Validate credentials (pseudo check)
if !isValidUser(creds.Username, creds.Password) {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "invalid credentials"})
}
// Generate new session ID and bind to user
sessionID, err := generateSessionID()
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "unable to create session"})
}
// Build payload that includes the fresh session ID
payload := fmt.Sprintf(`{"session":"%s","user":"%s","action":"login"}`, sessionID, creds.Username)
secret := []byte(os.Getenv("HMAC_SECRET"))
signature := computeHmac(payload, secret)
Set the cookie with the new session ID and return the signed payload (or store server-side and return a reference).
// Set secure, HttpOnly cookie with the new session ID
cookie := &http.Cookie{
Name: "session",
Value: sessionID,
Path: "/",
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
}
c.Cookie(cookie)
// Return the signed payload for client-side storage or immediate use
return c.JSON(fiber.Map{
"session": sessionID,
"action": "login",
"signature": signature,
})
}
For subsequent authenticated requests, the client should present the session ID and a signature computed over the intended action, and the server must recompute the Hmac over the exact payload (including the session ID) to verify authenticity. This ensures that if an attacker fixes a session identifier before authentication, the signature for that identifier will not be valid after the legitimate user rotates the session.
Additional hardening steps include:
- Binding the session identifier to the user ID and including it in every signed payload.
- Using short-lived signatures or including a timestamp/nonce to prevent replay.
- Invalidating the server-side session store on logout and on password change.
Tools like middleBrick can validate these patterns by scanning endpoints and checking whether session identifiers are rotated after login and included within the signed data scope.