Replay Attack in Fiber with Basic Auth
Replay Attack in Fiber with Basic Auth — how this specific combination creates or exposes the vulnerability
A replay attack in the context of Fiber with Basic Auth occurs when an attacker intercepts a valid HTTP request containing Base64-encoded credentials and re-sends it to the server to gain unauthorized access. Because Basic Auth transmits credentials in an easily reversible format and does not include built-in protection against replay, the same request can be replayed to produce the same authenticated effect if no additional countermeasures are in place.
In Fiber, routes often rely on middleware for authentication. If you use Basic Auth without additional protections such as nonces, timestamps, or one-time tokens, an attacker who captures a request—e.g., via a compromised network segment or insecure logging—can replay it. Consider a login route that returns a session-like success without enforcing one-time use or channel binding. An attacker can replay the identical Authorization header to the same endpoint and receive the same response, effectively impersonating the victim user.
For example, an intercepted request might include the header Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ= (username:password). If the server treats this as a valid, repeatable authentication token without additional validation, the replayed request will be accepted. This is especially risky when the endpoint performs privileged actions (e.g., changing email or password) and does not validate additional context such as request origin, idempotency keys, or freshness indicators.
Because Basic Auth does not bind the credential to a specific session or include a challenge-response mechanism, the onus is on the application to introduce replay resistance. Without such controls, the combination of Fiber and Basic Auth can expose APIs to replay, particularly when requests are transmitted over insecure channels or when logging mechanisms inadvertently expose headers.
Basic Auth-Specific Remediation in Fiber — concrete code fixes
To mitigate replay risks while using Basic Auth in Fiber, combine secure credential handling with replay-resistant patterns. Always enforce HTTPS to protect credentials in transit, and avoid sending sensitive operations over unencrypted connections. Introduce server-side nonce or timestamp validation to ensure each request is unique and time-bound.
Below is a secure Basic Auth middleware implementation in Fiber that includes timestamp and nonce-like handling using a server-side cache (e.g., Redis) to track recently seen values. This example demonstrates how to reject requests with stale or reused nonces.
package main
import (
"crypto/subtle"
"encoding/base64"
"net/http"
"strings"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/requestid"
)
// In-memory store for demo; replace with Redis or similar in production.
var seenNonces = make(map[string]time.Time)
func basicAuthMiddleware(c *fiber.Ctx) error {
// Require HTTPS in production; reject cleartext credentials on non-TLS.
if c.Protocol() != "https" {
return c.Status(http.StatusForbidden).SendString("require HTTPS")
}
h := c.Get("Authorization")
if h == "" || !strings.HasPrefix(h, "Basic ") {
return c.SendStatus(http.StatusUnauthorized)
}
payload, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(h, "Basic "))
if err != nil {
return c.SendStatus(http.StatusUnauthorized)
}
// Parse username:password
parts := strings.SplitN(string(payload), ":", 2)
if len(parts) != 2 {
return c.SendStatus(http.StatusUnauthorized)
}
username, password := parts[0], parts[1]
// Validate credentials securely (use constant-time comparison).
const storedUser = "appuser"
const storedPass = "S3cur3P@ss!"
if subtle.ConstantTimeCompare([]byte(username), []byte(storedUser)) != 1 ||
subtle.ConstantTimeCompare([]byte(password), []byte(storedPass)) != 1 {
return c.SendStatus(http.StatusUnauthorized)
}
// Replay resistance: require a client-supplied nonce and timestamp.
nonce := c.Get("X-Nonce")
tsHeader := c.Get("X-Timestamp")
if nonce == "" || tsHeader == "" {
return c.Status(http.StatusBadRequest).SendString("missing nonce or timestamp")
}
// Enforce a small time window (e.g., 2 minutes) to prevent replay.
var ts int64
if _, err := fmt.Sscanf(tsHeader, "%d", &ts); err != nil || time.Since(time.Unix(ts, 0)) > 2*time.Minute {
return c.Status(http.StatusForbidden).SendString("stale request")
}
// Check for nonce reuse.
if seen, exists := seenNonces[nonce]; exists && time.Since(seen) < 5*time.Minute {
return c.Status(http.StatusForbidden).SendString("replayed nonce")
}
seenNonces[nonce] = time.Now()
// Proceed with request (consider scoping nonce to user/session in production).
return c.Next()
}
func main() {
app := fiber.New()
app.Use(requestid.New())
app.Use(basicAuthMiddleware)
app.Get("/secure", func(c *fiber.Ctx) error {
return c.SendString("authenticated")
})
app.Listen(":3000")
}
Key points:
- Always use HTTPS to prevent on-path interception of the Authorization header.
- Add per-request nonces or timestamps with a short validity window to make replayed requests detectable.
- Track recently seen nonces in a fast, TTL-backed store to reject reuse within the validity window.
- Prefer constant-time comparison for credentials to avoid timing attacks.
These steps reduce the risk of replay while keeping Basic Auth as the transport identity mechanism. For higher assurance, consider migrating to token-based flows with built-in replay resistance (e.g., OAuth 2.0 with Proof Key for Code Exchange), but the above hardens Basic Auth within Fiber’s constraints.