HIGH phishing api keysfiberjwt tokens

Phishing Api Keys in Fiber with Jwt Tokens

Phishing API Keys in Fiber with JWT Tokens — how this specific combination creates or exposes the vulnerability

In a Fiber-based API, embedding a long-term API key inside a JWT token payload or using the token as a bearer credential can expose the key to phishing and extraction attacks. A common pattern is to issue a JWT at authentication and then pass it in an Authorization header (Bearer) for subsequent calls. If the token contains the API key as a claim (e.g., sub, a custom field, or in the payload without encryption), an attacker who can trick a user into visiting a malicious site may be able to capture the token via phishing, logs, or insecure client-side storage. Because JWTs are often base64Url-encoded rather than encrypted by default, the embedded API key can be decoded trivially if the token is intercepted.

Consider a scenario where a Fiber route issues a JWT that includes the API key:

// Example: JWT includes the API key as a claim (vulnerable pattern)
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()

	app.Post("/login", func(c *fiber.Ctx) error {
		// In a real app, validate credentials first
		apiKey := "sk_live_abc123" // sensitive key embedded in JWT
		token := jwt.New(jwt.SigningMethodHS256)
		claims := token.Claims.(jwt.MapClaims)
		claims["api_key"] = apiKey
		claims["exp"] = time.Now().Add(time.Hour).Unix()
		tokenString, _ := token.SignedString([]byte("weak_secret"))
		return c.JSON(fiber.Map{ "token": tokenString })
	})

	app.Listen(":3000")
}

If an attacker conducts a phishing campaign (e.g., a fake login page) and lures a victim to authenticate, the victim’s JWT containing the API key can be stolen. The attacker can then decode the base64Url payload (no signature verification needed to read claims) to recover the API key. Even if signed, a weak secret or token leakage in logs, browser history, or referrer headers enables replay. Because the API key is now outside a secure server-side vault and embedded in a token, phishing becomes a viable extraction path.

Additionally, if the API key is used as the signing secret for the JWT (a dangerous anti-pattern), compromising the key enables forging tokens; conversely, if the token is accepted as a bearer credential across multiple services, a single phishing success can propagate access across systems. This combination increases the blast radius compared to isolated key storage.

JWT Tokens-Specific Remediation in Fiber — concrete code fixes

To mitigate phishing risks when using JWTs with API keys in Fiber, avoid embedding sensitive API keys in token payloads. Instead, keep API keys server-side, use short-lived tokens, and enforce strict transport and storage protections.

Remediation 1: Do not include API keys in JWT claims. Store API keys in a secure server-side store (e.g., environment variables, secret manager) and reference them by an opaque token or session mapping. Issue JWTs with minimal claims and use them only for authentication/authorization, not as key transport.

// Secure: JWT does NOT contain the API key
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()

	app.Post("/login", func(c *fiber.Ctx) error {
		// Validate credentials against a secure store
		// Issue a JWT without sensitive data
		token := jwt.New(jwt.SigningMethodHS256)
		claims := token.Claims.(jwt.MapClaims)
		claims["sub"] = "user-123"
		claims["scope"] = "read write"
		claims["exp"] = time.Now().Add(time.Minute * 15).Unix()
		tokenString, _ := token.SignedString([]byte("a_very_strong_secret"))
		return c.JSON(fiber.Map{ "token": tokenString })
	})

	app.Get("/protected", jwt.New(jwt.Config{
		SigningKey: jwt.SigningKeyFunc(func(token *jwt.Token) (interface{}, error) {
			return []byte("a_very_strong_secret"), nil
		}),
	}), func(c *fiber.Ctx) error {
		// Use server-side mapping to associate token with API key
		userID := c.Locals("user").(*jwt.Token).Claims.(jwt.MapClaims)["sub"].(string)
		apiKey, err := fetchAPIKeyFromSecureStore(userID)
		if err != nil {
			return c.SendStatus(fiber.StatusUnauthorized)
		}
		// Use apiKey for downstream service calls
		return c.JSON(fiber.Map{ "status": "ok" })
	})

	app.Listen(":3000")
}

func fetchAPIKeyFromSecureStore(userID string) (string, error) {
	// Example: retrieve from environment or secret manager
	// In production, use a secure secret store
	if userID == "user-123" {
		return "sk_live_abc123", nil
	}
	return "", fmt.Errorf("not found")
}

Remediation 2: Use short-lived tokens and strict validation. Set short expiration times, enforce HTTPS, and validate token signatures rigorously. Rotate signing keys periodically and avoid using the same key across environments.

// Example of strict validation and short-lived token usage
app.Get("/resource", jwt.New(jwt.Config{
	SigningKey: jwt.SigningKeyFunc(func(token *jwt.Token) (interface{}, error) {
		// Verify algorithm to prevent alg:none attacks
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return []byte("a_very_strong_secret"), nil
	}),
	TokenLookup: "header: Authorization",
	AuthScheme:  "Bearer",
}), func(c *fiber.Ctx) error {
	// Token is validated automatically; claims are available via c.Locals("claims")
	claims := c.Locals("claims").(jwt.MapClaims)
	// Perform scope/role checks here
	return c.JSON(fiber.Map{ "data": "secure resource" })
})

These changes ensure that API keys remain server-side, JWTs carry only necessary authorization claims, and tokens are short-lived and strictly validated, reducing the impact of phishing.

Frequently Asked Questions

Why is embedding API keys in JWT claims risky even if the token is signed?
Because JWTs are often base64Url-encoded, the payload can be decoded without the secret to read embedded API keys. If a token is leaked via phishing, logs, or browser storage, the key is exposed. Signed tokens prevent tampering but do not prevent disclosure of sensitive claims.
What is a safer alternative to embedding API keys in JWTs in Fiber applications?
Keep API keys in a secure server-side store (e.g., environment variables or a secret manager) and issue JWTs that contain only non-sensitive claims like user ID and scopes. Use short-lived tokens, enforce HTTPS, and map tokens to keys server-side when calling downstream services.