Session Fixation in Fiber with Firestore
Session Fixation in Fiber with Firestore — how this specific combination creates or exposes the vulnerability
Session fixation occurs when an application allows an attacker to force a user to use a known session identifier. In a Fiber application that relies on Firestore to store session state, this risk arises when session identifiers are created or accepted without validation or rotation. Because Firestore is often used as a backend datastore for user sessions or authentication metadata, misconfigured handlers or insecure session management logic can expose session tokens stored in Firestore records.
Consider a typical Fiber app that stores session data in a Firestore collection named sessions. If the server generates a session ID only once (e.g., on login) and stores it in Firestore without invalidating previous identifiers or binding the session to a client context (such as IP or user-agent), an attacker can craft a URL with a known session ID. When the victim authenticates, the server writes a fresh session document to Firestore keyed by the attacker-chosen ID. Later, the attacker uses that ID to hijack the authenticated session. Because Firestore documents are directly referenced by ID, predictable or reused keys make this easier to exploit.
Another vector involves insecure deserialization or improper session cleanup. If a Fiber route reads a session document from Firestore based on a client-supplied cookie without verifying ownership or freshness, an attacker can reuse an old session document reference. For example, if the application does not enforce one-session-per-user or does not rotate the session identifier after login, the fixed session ID remains valid across authentication boundaries. This violates secure session management practices and enables account takeover even when other protections are in place.
The interaction with Firestore can also amplify risks if security rules are misconfigured. Overly permissive Firestore rules might allow read or write access to session documents based solely on document ID, without validating that the requesting user is the rightful owner. In such cases, an attacker who knows or guesses a session ID can read or modify session data directly through Firestore, bypassing application-level checks. Therefore, session-related documents must be protected with rule-based ownership checks and must not rely on ID predictability or static values.
Firestore-Specific Remediation in Fiber — concrete code fixes
To mitigate session fixation in a Fiber application that uses Firestore, implement strict session lifecycle controls, validate binding context, and enforce secure Firestore document access patterns. Always generate cryptographically random session identifiers and rotate them after authentication. Store session metadata in Firestore with ownership fields such as user ID and creation timestamp, and validate these fields on every request instead of trusting the session ID alone.
Below is a secure example of creating and validating a session in Fiber with Firestore integration. The code uses strong randomness for session IDs, binds sessions to user metadata, and verifies ownership on each request.
// session.go
package main
import (
"context"
"crypto/rand"
"encoding/base64"
"errors"
"net/http"
"github.com/gofiber/fiber/v2"
"cloud.google.com/go/firestore"
)
type Session struct {
UserID string `firestore:"userId"`
CreatedAt time.Time `firestore:"createdAt"`
UserAgent string `firestore:"userAgent"`
IP string `firestore:"ip"`
}
func generateSessionID() (string, error) {
b := make([]byte, 32)
_, err := rand.Read(b)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(b), nil
}
func createSession(ctx context.Context, client *firestore.Client, userID, userAgent, ip string) (string, error) {
sid, err := generateSessionID()
if err != nil {
return "", err
}
sessionRef := client.Collection("sessions").Doc(sid)
_, err = sessionRef.Set(ctx, Session{
UserID: userID,
CreatedAt: time.Now().UTC(),
UserAgent: userAgent,
IP: ip,
})
if err != nil {
return "", err
}
return sid, nil
}
func validateSession(ctx context.Context, client *firestore.Client, c *fiber.Ctx) (*Session, error) {
sid := c.Cookies("session_id")
if sid == "" {
return nil, errors.New("missing session cookie")
}
var sess Session
doc, err := client.Collection("sessions").Doc(sid).Get(ctx)
if err != nil || !doc.Exists() {
return nil, errors.New("invalid session")
}
if err := doc.DataTo(&sess); err != nil {
return nil, err
}
// Bind-check: ensure request context matches session context
if sess.UserAgent != c.Get("User-Agent") || sess.IP != c.IP() {
return nil, errors.New("session context mismatch")
}
return &sess, nil
}
In this example, the session ID is generated using crypto/rand, making it unpredictable and resistant to fixation attempts. Each session document in Firestore includes binding metadata such as user agent and IP, and validation enforces that these match the current request. This ensures that even if an attacker forces a session ID, they cannot reuse it from a different context.
Additionally, configure Firestore security rules to enforce ownership checks. The following rules ensure that session documents can only be accessed by the authenticated user associated with the session, preventing unauthorized reads or modifications based on known IDs:
// Firestore rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /sessions/{sessionId} {
allow read, write: if request.auth != null && request.auth.uid == request.resource.data.userId;
}
}
}
These rules complement application-level checks by ensuring that only the user identified by userId can interact with their own session document. Combined with session rotation after login and strict context validation, this approach significantly reduces the risk of session fixation in Fiber applications backed by Firestore.