HIGH cross site request forgerybuffalo

Cross Site Request Forgery in Buffalo

How Cross Site Request Forgery Manifests in Buffalo

Cross-Site Request Forgery (CSRF) is an attack that tricks a user's browser into executing an unwanted action on a trusted site where they are authenticated. In Buffalo applications, CSRF vulnerabilities typically arise from one of two patterns: either state-changing endpoints (POST, PUT, PATCH, DELETE) lack proper anti-CSRF token validation, or the token validation is present but incorrectly implemented or bypassed.

Buffalo, by default, does not enable CSRF protection for all routes. Developers must explicitly apply the csrf.Middleware to their action or group of actions. A common misconfiguration is applying CSRF protection only to HTML form-submitting routes while forgetting that modern single-page applications (SPAs) or API clients also make state-changing requests (e.g., via fetch or axios) that are equally vulnerable.

Consider this vulnerable Buffalo action that processes a funds transfer. It handles a POST request but has no CSRF checks:

// actions/transfer.go
func Transfer(c buffalo.Context) error {
    // Get the authenticated user from session
    user := c.Value("current_user").(*models.User)

    // Bind the request body
    var payload struct {
        ToAccount string `json:"to_account"`
        Amount    int    `json:"amount"`
    }
    if err := c.Bind(&payload); err != nil {
        return c.Error(400, err)
    }

    // Perform the transfer logic...
    // ...

    return c.Render(200, r.JSON(map[string]string{
        "status": "success",
    }))
}

An attacker can host a malicious page that auto-submits a forged request to this endpoint. If a logged-in user visits the page, their browser includes session cookies (e.g., session ID) automatically, and the transfer executes without the user's intent.

Another Buffalo-specific pattern involves the incorrect use of the csrf.Token helper in templates. If a template renders a form but the developer manually constructs the HTML without using Buffalo's form helpers or forgets to inject the token, the protection is absent:

<!-- VULNERABLE: Manual form without CSRF token -->
<form action="/transfer" method="POST">
    <input name="to_account">
    <input name="amount" type="number">
    <button type="submit">Send Money</button>
</form>

Buffalo's CSRF middleware expects a valid token in either the X-CSRF-Token header (for APIs) or a hidden form field named csrf_token (for HTML forms). Missing either bypasses the check if the middleware is not applied, or the request will be rejected if it is applied. The vulnerability lies in the absence of the middleware or the token itself.

For JSON APIs, a frequent mistake is applying CSRF middleware but then making client-side requests that fail to include the required header. This leads to false positives in scans, but the underlying risk remains if an attacker can force a user's browser to make a request with the correct header (possible via a form with an onload submit script targeting an API endpoint that accepts application/x-www-form-urlencoded).

Buffalo-Specific Detection

Detecting CSRF in a Buffalo application involves checking for two things: (1) whether state-changing endpoints have the csrf.Middleware applied, and (2) whether client-side requests (forms, API calls) correctly include the anti-CSRF token. Manual code review can find missing middleware in actions/ files or route definitions, but automated scanning is more efficient.

Static analysis looks for the absence of csrf.Middleware on actions handling POST, PUT, PATCH, or DELETE. In Buffalo, routes are often defined in actions/app.go or via c.Resource. A resource route like c.Resource(&User{}) auto-generates CRUD endpoints; you must ensure the underlying actions are protected or wrap the resource in the middleware group.

Dynamic (runtime) scanning tests the actual endpoint behavior. A scanner will:

  • Identify an authenticated session (by logging in or using a provided session cookie).
  • Issue a state-changing request (e.g., POST) without a CSRF token and observe if the server rejects it (HTTP 403 or 400) or processes it.
  • If the request succeeds, the endpoint is vulnerable.

middleBrick's scanner automates this detection. It submits a URL, follows redirects to establish a session if a login page is detected, and then probes all discovered POST/PUT/PATCH/DDELETE endpoints. It checks for the presence of a Set-Cookie header for a CSRF token (often named _buffalo_csrf) and validates that subsequent requests without the token are rejected. It also parses HTML responses for forms lacking a hidden csrf_token field.

To scan your Buffalo API with middleBrick, use the CLI tool:

middlebrick scan https://your-buffalo-app.com/api

The report will flag any state-changing endpoint that accepts the request without a valid CSRF token as a High severity finding under the "Authentication" or "Input Validation" category, with specific Buffalo context. The web dashboard tracks these scores over time, and the GitHub Action can fail a PR if a new CSRF issue is introduced.

Buffalo-Specific Remediation

Remediating CSRF in Buffalo involves consistently applying the built-in CSRF middleware and ensuring tokens are included in all state-changing requests. Buffalo uses the github.com/gorilla/csrf library under the hood, exposed via github.com/gobuffalo/buffalo/middleware.

1. Apply CSRF Middleware Globally or to Groups

The safest approach is to apply CSRF protection to all non-GET/HEAD/OPTIONS routes. In actions/app.go:

// actions/app.go
package actions

import (
    "github.com/gobuffalo/buffalo"
    "github.com/gobuffalo/buffalo/middleware"
)

func App() buffalo.App {
    app := buffalo.New(buffalo.Options{
        // ... other options ...
    })

    // Apply CSRF middleware to all routes except safe methods
    app.Use(middleware.CSRF("_buffalo_csrf"))

    // ... route definitions ...

    return app
}

Alternatively, apply it to a specific group (e.g., all API routes under /api/v1):

api := app.Group("/api/v1")
api.Use(middleware.CSRF("_buffalo_csrf"))
// Then define API routes on 'api'

2. Include Tokens in HTML Forms

When using Buffalo's form helpers (via buffalo/form or the f template variable), the CSRF token is automatically included. For manual forms, use the csrf.Template helper in your templates:

<!-- templates/transfer.html -->
<form action="/transfer" method="POST">
    <%= csrf.Template(c) %>
    <input name="to_account">
    <input name="amount" type="number">
    <button type="submit">Send Money</button>
</form>

3. Include Tokens in API Requests (SPA/mobile clients)

For JSON APIs, the client must read the CSRF cookie and send its value in the X-CSRF-Token header. First, ensure the middleware is configured to set the cookie. The default middleware sets a cookie named _buffalo_csrf. In your JavaScript client:

// Example using fetch
async function transfer(to, amount) {
    // Get the CSRF token from the cookie
    const csrfToken = document.cookie
        .split('; ')
        .find(row => row.startsWith('_buffalo_csrf='))
        ?.split('=')[1];

    const response = await fetch('/transfer', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRF-Token': csrfToken // Required header
        },
        body: JSON.stringify({ to_account: to, amount: amount })
    });
    return response.json();
}

4. Configure Cookie Settings (Optional but Recommended)

You can customize the CSRF cookie options (e.g., HttpOnly, Secure, SameSite) when creating the middleware. This is done in the middleware setup:

app.Use(middleware.CSRF("_buffalo_csrf", func(c *csrf.Cookie) {
    c.HttpOnly = true
    c.Secure = true // Only over HTTPS
    c.SameSite = http.SameSiteStrictMode
}))

Setting SameSite=Strict or Lax provides additional defense-in-depth against CSRF, but the token mechanism remains the primary control.

After applying these fixes, re-scan with middleBrick to confirm the CSRF findings are resolved. The scanner will validate that endpoints now reject requests without a valid token.

Frequently Asked Questions

Does Buffalo's default setup protect against CSRF?
No. Buffalo does not enable CSRF middleware by default. Developers must explicitly add middleware.CSRF() to their application or route groups. Forgetting to do so on state-changing endpoints leaves the application vulnerable.
How does middleBrick's CSRF detection work with Buffalo's token format?
middleBrick understands Buffalo's default CSRF cookie name (_buffalo_csrf) and token placement (hidden form field or X-CSRF-Token header). It dynamically tests endpoints by omitting the token and observing if the request succeeds, providing a real-world assessment of the vulnerability.