Cross Site Request Forgery in Gin
How Cross Site Request Forgery Manifests in Gin
Cross-Site Request Forgery (CSRF) is an attack that tricks a user's browser into executing an unintended action on a trusted site where they are authenticated. In Gin applications, this vulnerability arises when state-changing HTTP methods (POST, PUT, DELETE, PATCH) are handled without verifying a user's intentionality via an anti-forgery token. Gin, as a minimalist HTTP framework, does not include built-in CSRF protection. Developers must explicitly integrate middleware to validate tokens, leaving a common attack surface.
A typical vulnerable Gin route might directly process a form submission or API call without token checks:
func main() {
r := gin.Default()
// Vulnerable endpoint: no CSRF protection
r.POST("/transfer", func(c *gin.Context) {
var req TransferRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// Logic to transfer funds from the authenticated user's account
processTransfer(c, req)
c.JSON(200, gin.H{"status": "success"})
})
r.Run()
}An attacker can host a malicious page that auto-submits a forged request to /transfer using the victim's browser. If the victim is logged into the Gin application (e.g., via session cookie), the browser automatically includes authentication credentials, and the server processes the request as legitimate. This is especially critical for banking, e-commerce, or any application performing sensitive actions via API.
Gin-specific manifestations often involve:
- Missing middleware: Forgetting to apply CSRF middleware globally or to specific routes.
- API-only endpoints: Assuming stateless JWT authentication negates CSRF risk. While JWTs in Authorization headers are immune (as browsers don't send them automatically), many Gin apps still use cookies for session management or hybrid auth, creating exposure.
- Inconsistent protection: Applying CSRF checks only to HTML form routes but not to JSON API endpoints that also change state.
- Token exposure: Serving the CSRF token via a GET endpoint (e.g.,
/csrf-token) without proper same-site cookie attributes, allowing token theft via XSS or referrer leaks.
OWASP classifies this as OWASP API Security Top 10 2023 – A02:2023 – Authentication Broken (when CSRF bypasses authentication checks) and A01:2023 – Broken Object Level Authorization if the forged request targets another user's resource via predictable IDs (BOLA/IDOR). A real-world example is CVE-2021-3287, where a Node.js app's missing CSRF token allowed account takeover via forged password change requests.
Gin-Specific Detection
Detecting CSRF in Gin applications requires analyzing both the application's configuration and its runtime behavior. middleBrick's security scan includes a dedicated Authentication check that tests for CSRF vulnerabilities by simulating cross-origin requests and inspecting responses for missing or misconfigured anti-forgery mechanisms.
During a scan, middleBrick will:
- Send a state-changing request (e.g., POST to a suspected endpoint) without a CSRF token and observe if the server accepts it.
- Check for the presence of
Set-Cookieheaders withSameSiteattributes (Strict or Lax) that mitigate CSRF. - Inspect OpenAPI/Swagger specifications (if provided) for documented security schemes that might indicate expected CSRF tokens (e.g.,
csrfTokenheader). - Test for token reflection vulnerabilities by submitting a request with a fake token and checking if the token is echoed in responses or used in subsequent requests without validation.
For Gin-specific detection, you can also manually:
- Review middleware chains: Ensure routes use a CSRF middleware like
github.com/gorilla/csrf. - Check for token generation in templates: In server-rendered Gin apps, the token should be injected into HTML forms via
csrf.TemplateField(r). - Verify API endpoints: For JSON APIs, the token should be required in a header (e.g.,
X-CSRF-Token) and validated server-side.
Using middleBrick's CLI tool, you can scan a Gin API endpoint from your terminal:
middlebrick scan https://your-gin-api.comThe resulting report will include a per-category breakdown. A missing CSRF implementation will appear under the Authentication category with a high severity finding, including the exact endpoint tested, the request method, and evidence (e.g., "POST request accepted without CSRF token"). You can also integrate this scan into your GitHub Action to fail PRs if CSRF issues are introduced:
uses: middlebrick/scan-action@v1
with:
api_url: ${{ env.API_URL }}
fail_on_score: 'B'
This proactive detection helps catch misconfigurations before deployment.
Gin-Specific Remediation
Remediating CSRF in Gin requires implementing token-based validation. The standard approach uses the gorilla/csrf middleware, which is framework-agnostic and works seamlessly with Gin.
Step 1: Install and configure the middleware
go get github.com/gorilla/csrfStep 2: Apply middleware globally or to specific routes
For global protection (recommended for apps with sessions):
func main() {
r := gin.Default()
// Configure CSRF
csrfMiddleware := csrf.Protect(
[]byte("32-byte-long-auth-key"), // replace with a secure random key
csrf.Secure(false), // set to true in production (HTTPS)
csrf.HttpOnly(true),
csrf.Path("/"),
csrf.SameSite(csrf.SameSiteLaxMode),
)
r.Use(csrfMiddleware)
// All routes now require CSRF token for "unsafe" methods
r.POST("/transfer", transferHandler)
r.Run()
}Step 3: Inject token into client-side requests
For server-rendered HTML (using Gin's HTML templates):
func transferForm(c *gin.Context) {
c.HTML(200, "transfer.html", gin.H{
"csrfToken": csrf.Token(c),
})
}In the transfer.html template:
<form action="/transfer" method="POST">
<input type="hidden" name="csrf_token" value="{{ .csrfToken }}">
<!-- other fields -->
</form>For JSON APIs (SPA/mobile clients), the token must be sent in a header. First, expose an endpoint to fetch the token (protect it with same-site cookies):
r.GET("/csrf-token", func(c *gin.Context) {
c.JSON(200, gin.H{"token": csrf.Token(c)})
})Then, the client includes it in subsequent state-changing requests:
// Client-side JavaScript example
fetch('/transfer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken // from /csrf-token response
},
body: JSON.stringify({ amount: 100 })
});Important considerations for Gin:
- Stateless JWT APIs: If your Gin API uses JWTs exclusively in the
Authorizationheader (no cookies), CSRF is not a risk because browsers don't automatically attach JWTs to cross-origin requests. However, if you use cookies for any authentication, CSRF protection is mandatory. - Exempt safe methods: CSRF middleware should only protect state-changing methods (POST, PUT, DELETE, PATCH). GET requests must remain idempotent and safe, per HTTP specs.
- SameSite cookies: Set
SameSite=StrictorLaxon session cookies as a defense-in-depth measure. This is configured in your session middleware (e.g.,gin-contrib/sessions).
After remediation, rescan with middleBrick's Web Dashboard to verify the Authentication score improves. The Pro plan's continuous monitoring can alert you if CSRF protection regresses in future deployments. Remember: middleBrick detects and reports; it does not fix code. These remediation steps must be implemented in your Gin application codebase.