HIGH cross site request forgeryginhmac signatures

Cross Site Request Forgery in Gin with Hmac Signatures

Cross Site Request Forgery in Gin with Hmac Signatures — how this combination creates or exposes the vulnerability

Cross Site Request Forgery (CSRF) leverages the trust a web application places in an authenticated user’s browser. In Go frameworks such as Gin, developers sometimes rely solely on HMAC-based signatures (for example, an HMAC-SHA256 of request data) to provide integrity and authenticity. While HMAC signatures can protect against tampering in some scenarios, they do not automatically prevent CSRF when the signature mechanism is not tied to a per-user, per-session binding and when endpoints accept state-changing requests without verifying the request origin.

Consider a Gin endpoint that processes a money transfer using an HMAC signature to ensure the payload has not been altered. If the signature is computed over parameters like account and amount but does not include a per-user token or a same-site/cookie binding, an attacker can craft a malicious HTML page that submits a request with a valid HMAC. The attacker only needs to know or guess the parameters and the signing algorithm; if the victim is authenticated and the endpoint relies only on the HMAC (without a CSRF token or SameSite cookies), the request may be executed with the victim’s privileges. This is because HMAC signatures do not by themselves bind the request to a browser context; they ensure data integrity but not request origin.

A concrete pattern that creates risk looks like this: a client computes an HMAC over JSON or form values using a shared secret, sends it in a header (e.g., X-Signature), and the Gin handler verifies the signature before performing the action. If the handler does not check the Origin or Referer header, does not enforce SameSite cookie attributes, and does not require a per-session CSRF token, an attacker can host a form that replicates the required parameters and signature generation logic (or guesses/obtains a valid signature for known inputs). Because the endpoint trusts the HMAC alone, the forged request is processed as if it were legitimate.

In real-world scenarios, this can map to findings such as those in the OWASP API Top 10 (e.g., Broken Object Level Authorization or missing access controls around state-changing methods). The absence of anti-CSRF measures alongside HMAC-based integrity checks means the API’s unauthenticated or weakly bound attack surface remains vulnerable to forged requests. Attack patterns like those seen in CVE-related web exploits illustrate how forged POST requests can perform actions without the user’s intent when origin verification is omitted.

To understand the exposure, think of HMAC signatures as ensuring that the payload has not been modified, but not ensuring that the request is intentionally initiated by the user. Without additional context — such as binding the signature to a per-session token, enforcing SameSite attributes, validating Origin, or requiring a separate CSRF token — the API remains exposed to CSRF even if every request carries a valid HMAC.

Hmac Signatures-Specific Remediation in Gin — concrete code fixes

To mitigate CSRF in Gin while using HMAC signatures, combine cryptographic integrity checks with anti-CSRF tokens and secure cookie practices. Below are concrete, realistic examples that demonstrate a safer approach.

1. Include a per-session CSRF token in the HMAC and validate it server-side

Generate a cryptographically random token per session, store it server-side (or in a signed, HttpOnly cookie), and include it in the HMAC computation. This binds the signature to the user’s session, preventing attackers from forging valid requests without the token.

// Example: server-side token generation and HMAC with CSRF token
package main

import (
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"math/big"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
)

func generateCSRFToken() (string, error) {
	const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	b := make([]byte, 32)
	for i := range b {
		n, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
		if err != nil {
			return "", err
		}
		b[i] = letters[int(n.Int64())%len(letters)]
	}
	return string(b), nil
}

func computeHMAC(data, secret, csrfToken string) string {
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(data + csrfToken))
	return hex.EncodeToString(mac.Sum(nil))
}

func main() {
	r := gin.Default()

	r.Use(func(c *gin.Context) {
		// On session creation, set a CSRF token in a signed cookie and also in secure session store
		csrfToken, _ := generateCSRFToken()
		// In practice, store csrfToken in server-side session associated with a session ID cookie
		c.Set("csrfToken", csrfToken)
		c.SetCookie("session_id", "session-abc123", 3600, "/", "", false, true)
		c.SetCookie("csrf_token", csrfToken, 3600, "/", "", false, true)
		c.Next()
	})

	r.POST("/transfer", func(c *gin.Context) {
		var payload struct {
			Account string `json:"account"`
			Amount  string `json:"amount"`
		}
		if err := c.BindJSON(&payload); err != nil {
			c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid body"})
			return
		}

		csrfToken, exists := c.Get("csrfToken")
		if !exists {
			c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "missing csrf token"})
			return
		}

		// Recompute expected signature from request body + CSRF token
		expectedMAC := computeHMAC(payload.Account+payload.Amount, "my-secret-key", csrfToken.(string))
		providedMAC := c.GetHeader("X-Signature")
		if !hmac.Equal([]byte(expectedMAC), []byte(providedMAC)) {
			c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "invalid signature"})
			return
		}

		// Additional origin/referer checks
		origin := c.GetHeader("Origin")
		if origin == "" {
			c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "missing origin"})
			return
		}
		// Optionally validate origin against a whitelist

		// Proceed with the action, knowing the request is authenticated and CSRF-protected
		c.JSON(http.StatusOK, gin.H{"status": "ok"})
	})

	r.Run()
}

2. Set SameSite and Secure cookie attributes and validate Origin/Referer

Ensure session and CSRF cookies use SameSite=Strict or Lax, Secure, and HttpOnly. Additionally, validate Origin or Referer headers on sensitive endpoints to defend against cross-origin forged requests.

// Example: secure cookie settings and origin validation in Gin
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()

	r.Use(func(c *gin.Context) {
		// Set secure cookies for session and CSRF token
		c.SetCookie("session_id", "session-abc123", 3600, "/", "example.com", true, true) // Secure: true, HttpOnly: true
		c.SetCookie("csrf_token", "random-csrf-value", 3600, "/", "example.com", true, true)
		c.Next()
	})

	r.POST("/transfer", func(c *gin.Context) {
		// Validate Origin to defend against CSRF from external sites
		origin := c.GetHeader("Origin")
		referer := c.GetHeader("Referer")
		allowedOrigin := "https://myapp.example.com"

		if origin != allowedOrigin && referer != allowedOrigin {
			c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "invalid origin"})
			return
		}

		// Verify signature and other checks here...
		c.JSON(http.StatusOK, gin.H{"status": "ok"})
	})

	r.Run()
}

3. Best-practice summary for Gin APIs using HMAC

  • Include a per-session CSRF token inside the HMAC computation so the signature is bound to the user’s session.
  • Use SameSite=Strict or Lax, Secure, and HttpOnly cookie attributes for session and CSRF cookies.
  • Validate Origin and/or Referer headers on state-changing endpoints as an additional layer.
  • Do not rely on HMAC alone to prevent CSRF; treat it as integrity protection, not an anti-CSRF mechanism.
  • Apply these measures to endpoints identified in the scan findings, especially those flagged under Authentication, BOLA/IDOR, and Unsafe Consumption checks.

Frequently Asked Questions

Does using HMAC signatures alone prevent CSRF in Gin APIs?
No. HMAC signatures ensure payload integrity but do not bind requests to a user’s browser context. Without per-session CSRF tokens, SameSite cookies, and origin validation, CSRF can still occur.
How does middleBrick help detect CSRF risks with HMAC-based APIs?
middleBrick runs checks such as Authentication, BOLA/IDOR, and Unsafe Consumption in parallel, testing the unauthenticated attack surface and providing prioritized findings with remediation guidance for CSRF and related issues.