HIGH cross site request forgeryfiberjwt tokens

Cross Site Request Forgery in Fiber with Jwt Tokens

Cross Site Request Forgery in Fiber with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Cross Site Request Forgery (CSRF) is an attack that tricks a victim into submitting an unwanted request on a web application in which they are authenticated. When using Jwt Tokens for authentication in Fiber, the typical pattern is to validate the token on each request, often via middleware, but if the application relies solely on the presence of a valid JWT and does not enforce anti-CSRF protections, state-changing requests can be forged from malicious origins.

Consider a Fiber endpoint that performs a sensitive action such as changing an email or updating a user profile. If the route only checks for a valid JWT in the Authorization header and does not verify the request origin, an attacker can craft a form on a malicious site that submits a request to that endpoint. When the victim is logged in and their browser automatically includes cookies (if any session-like state exists) or if the JWT is stored in a way that the browser attaches it automatically (e.g., in local storage and sent via Authorization header by custom client code), the request may be executed with the victim’s privileges.

In a typical Fiber setup with Jwt Tokens, the server decodes and validates the token, often using a middleware similar to:

func JWTMiddleware(c *fiber.Ctx) error {
    auth := c.Get("Authorization")
    if auth == "" {
        return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Authorization header required"})
    }
    tokenString := strings.TrimPrefix(auth, "Bearer ")
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected signing method")
        }
        return []byte(secretKey), nil
    })
    if err != nil || !token.Valid {
        return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid token"})
    }
    c.Locals("user", token.Claims.(jwt.MapClaims)["sub"])
    return c.Next()
}

If the client sends the Authorization header manually (e.g., via JavaScript using fetch with credentials omitted), the browser will not automatically include the JWT in cross-origin requests, which provides some protection. However, if the server sets CORS headers that allow credentials or if the JWT is also stored in an accessible cookie (for example, to support older clients), a forged request from a malicious site can include the necessary headers or cookies, leading to CSRF.

Additionally, if the application provides an endpoint that returns sensitive data and that endpoint is vulnerable to JSONP or other forms of cross-origin data leakage, an attacker may be able to trick the victim’s browser into making authenticated requests and capturing the responses. This is especially relevant when Jwt Tokens are used without proper CORS and CSRF mitigations, as the token may be accepted regardless of the request origin.

To illustrate a vulnerable route in Fiber that could be exploited via CSRF when Jwt Tokens are used without additional protections:

app.Post("/change-email", func(c *fiber.Ctx) error {
    var req struct {
        Email string `json:"email"`
    }
    if err := c.BodyParser(&req); err != nil {
        return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request"})
    }
    userID := c.Locals("user").(string)
    // Update email for userID without origin or CSRF token check
    if err := updateEmailInStore(userID, req.Email); err != nil {
        return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Could not update email"})
    }
    return c.JSON(fiber.Map{"status": "ok"})
})

An attacker can host a page that sends a POST request to /change-email with a forged email. If the victim’s browser includes the Authorization header or a cookie with the JWT, the request succeeds, demonstrating CSRF in the context of Jwt Tokens in Fiber.

Jwt Tokens-Specific Remediation in Fiber — concrete code fixes

Remediation focuses on ensuring that requests are intentionally initiated by the user and not forged. With Jwt Tokens in Fiber, you should combine token validation with CSRF-specific defenses, such as same-site cookies, anti-CSRF tokens, or strict CORS policies.

One effective approach is to require a custom header (e.g., X-Requested-With) on state-changing requests, which cannot be set cross-origin due to browser security policies. This is not a foolproof defense for all scenarios but adds a layer when combined with other measures.

Below is a hardened middleware example for Fiber that checks the JWT and also validates a custom header to help mitigate CSRF:

func SecureMiddleware(c *fiber.Ctx) error {
    auth := c.Get("Authorization")
    if auth == "" {
        return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Authorization header required"})
    }
    tokenString := strings.TrimPrefix(auth, "Bearer ")
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected signing method")
        }
        return []byte(secretKey), nil
    })
    if err != nil || !token.Valid {
        return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid token"})
    }
    // Require a custom header to help prevent CSRF
    if c.Get("X-Requested-With") != "XMLHttpRequest" {
        return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "CSRF protection: invalid request origin"})
    }
    c.Locals("user", token.Claims.(jwt.MapClaims)["sub"])
    return c.Next()
}

For APIs consumed by browsers, it is recommended to set the SameSite attribute on cookies and avoid storing Jwt Tokens in cookies unless necessary. If you must store tokens in cookies, configure them as HttpOnly, Secure, and with SameSite=Strict or Lax. For JavaScript clients, keep tokens in memory or in non-cookie storage and explicitly set the Authorization header.

Another remediation pattern is to implement anti-CSRF tokens for forms or state-changing operations. The server can issue a short-lived CSRF token after authentication, and the client must include it in a header or form field. Here is an example of validating such a token in Fiber:

func CSRFMiddleware(c *fiber.Ctx) error {
    sessionToken := c.Cookies("csrf_token")
    requestToken := c.Get("X-CSRF-Token")
    if sessionToken == "" || requestToken != sessionToken {
        return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "Invalid CSRF token"})
    }
    return c.Next()
}

Ensure that CORS is configured to restrict origins and that credentials are only allowed for trusted origins. In Fiber, this can be done using CORS middleware with strict settings:

corsConfig := cors.Config{
    AllowOrigins:     "https://trusted.example.com",
    AllowCredentials: true,
    AllowHeaders:     "Authorization, Content-Type, X-Requested-With, X-CSRF-Token",
    AllowMethods:     "GET, POST, PUT, DELETE",
}
app.Use(cors.New(corsConfig))

By combining Jwt Token validation, strict CORS, same-site cookies, and anti-CSRF tokens or custom headers, you can significantly reduce the risk of CSRF in Fiber applications while still using Jwt Tokens for authentication.

Frequently Asked Questions

Does using Jwt Tokens in Fiber automatically prevent CSRF?
No. Jwt Tokens in headers do not prevent CSRF unless the client does not automatically attach them cross-origin. Without additional mitigations such as CORS restrictions, same-site cookies, or anti-CSRF tokens, forged requests can still be executed.
What is the most important mitigation for CSRF when using Jwt Tokens in Fiber?
The most important mitigation is to require explicit headers (such as a custom X-Requested-With or X-CSRF-Token) that cannot be set cross-origin by malicious sites, combined with strict CORS and same-site cookie attributes if cookies are used.