HIGH cross site request forgerygorilla muxbasic auth

Cross Site Request Forgery in Gorilla Mux with Basic Auth

Cross Site Request Forgery in Gorilla Mux with Basic Auth — how this specific combination creates or exposes the vulnerability

Cross Site Request Forgery (CSRF) occurs when an attacker tricks a victim’s browser into making an unwanted authenticated request. When Basic Auth is used with Gorilla Mux, the risk pattern changes compared to cookie-based sessions. Basic Auth typically relies on the browser sending an Authorization header on every request to the protected origin. If your Gorilla Mux routes are exposed to browser-based clients (for example, a JavaScript frontend calling an API endpoint), and no anti-CSRF protections are in place, a malicious site can construct forms or fetch requests that the browser will send automatically because the Authorization header is included for that origin.

In Gorilla Mux, routes are often defined with path variables and matchers (host, path prefixes, methods). Consider a route registered with a method like PUT and a path pattern such as /account/{id}/email. If this route only relies on Basic Auth (username:password in the Authorization header) and is reachable from a browser context, an attacker can host a page that submits a forged PUT or POST request to that endpoint. Because the browser includes the Authorization header automatically for same-origin or configured cross-origin requests (depending on CORS), the server may process the action as the authenticated user. This becomes more likely when developers expose admin or state-changing methods under predictable paths and permit cross-origin requests without proper CSRF or authorization checks beyond the presence of credentials.

Gorilla Mux itself does not enforce CSRF tokens or origin checks; it is a routing library. Therefore, the combination of Basic Auth plus browser-accessible endpoints places responsibility on the application to implement anti-CSRF measures. Common mistakes include allowing wide CORS origins with credentials, relying solely on the Authorization header, and failing to require custom headers or tokens that an attacker cannot easily set from a third-party site. Additionally, unsafe methods such as PUT or DELETE on user-modifiable resources should be treated as high-risk in this context, because they can be triggered by forged requests initiated from an attacker-controlled site. Even when Basic Auth is used, you must ensure that requests are intentionally initiated by the user, not by a malicious page in another tab.

Another subtlety with Gorilla Mux and Basic Auth involves preflight requests. Browsers issue OPTIONS preflight requests for cross-origin requests with custom headers or non-simple methods. If your server responds to OPTIONS with permissive CORS headers and allows credentials, you may inadvertently enable cross-origin authenticated requests that can be forged. Attackers do not need to read the response; they only need the browser to send the request with the Authorization header. Therefore, the server must validate Origin and Access-Control-Allow-Credentials carefully and avoid broad exposure of authenticated routes to any origin.

To contextualize the risk, this maps to the OWASP API Top 10 category for Broken Object Level Authorization and intersects with CSRF-like behavior when browser-based clients are involved. Unlike server-to-server calls, browser-mediated requests with Basic Auth require explicit defenses because the browser’s automatic header inclusion can be exploited. Effective mitigation involves requiring custom request headers, using anti-CSRF tokens for browser interactions, enforcing strict CORS policies, and avoiding state-changing methods for endpoints that can be invoked from browser JavaScript without additional safeguards.

Basic Auth-Specific Remediation in Gorilla Mux — concrete code fixes

Remediation focuses on ensuring that authenticated actions cannot be forged by malicious sites. Since Gorilla Mux does not provide built-in CSRF protection, you implement checks in your handlers and surrounding middleware. Below are concrete, syntactically correct examples using Basic Auth in Gorilla Mux with Go, followed by recommended patterns.

Example 1: Basic Auth handler with custom header validation

Require a custom header (for example, X-Requested-With) on state-changing methods. Because cross-origin forms cannot set arbitrary custom headers without CORS preflight approval, this mitigates simple CSRF attacks.

func secureHandler(w http.ResponseWriter, r *http.Request) {
    // Expect a custom header as a CSRF mitigator alongside Basic Auth
    const expected = "XMLHttpRequest"
    if r.Header.Get("X-Requested-With") != expected {
        http.Error(w, "forbidden", http.StatusForbidden)
        return
    }
    // Perform Basic Auth validation (pseudo check)
    user, pass, ok := r.BasicAuth()
    if !ok || !validateCredentials(user, pass) {
        w.Header().Set("WWW-Authenticate", `Basic realm="restricted"`)
        http.Error(w, "unauthorized", http.StatusUnauthorized)
        return
    }
    w.Write([]byte("ok"))
}

func validateCredentials(user, pass string) bool {
    // Replace with secure credential verification
    return user == "admin" && pass == "s3cr3t"
}

Example 2: Gorilla Mux route registration with method-specific handling

Register routes with Gorilla Mux and enforce CSRF-sensitive checks on unsafe methods while allowing safe methods like GET without extra tokens.

r := mux.NewRouter()
// Public read-only endpoint
r.HandleFunc("/public/info", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("public data"))
}).Methods("GET")

// Protected state-changing endpoint with CSRF-sensitive checks
r.HandleFunc("/account/{id}/email", func(w http.ResponseWriter, r *http.Request) {
    // Require custom header for non-GET requests
    if r.Method == "PUT" || r.Method == "POST" || r.Method == "DELETE" {
        if r.Header.Get("X-CSRF-Token") == "" {
            http.Error(w, "missing csrf token", http.StatusForbidden)
            return
        }
    }
    // Basic Auth validation
    user, pass, ok := r.BasicAuth()
    if !ok || !validateCredentials(user, pass) {
        w.Header().Set("WWW-Authenticate", `Basic realm="restricted"`)
        http.Error(w, "unauthorized", http.StatusUnauthorized)
        return
    }
    // Proceed with update logic
    vars := mux.Vars(r)
    id := vars["id"]
    // ... update email for id
    w.WriteHeader(http.StatusOK)
}).Methods("PUT", "POST", "DELETE")

http.ListenAndServe(":8080", r)

Example 3: CORS configuration to limit exposure

Configure CORS to avoid allowing credentials from arbitrary origins. This prevents attackers from leveraging cross-origin authenticated requests.

func corsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        origin := r.Header.Get("Origin")
        // Only allow known frontend origins
        allowed := map[string]bool{"https://app.example.com": true}
        if allowed[origin] {
            w.Header().Set("Access-Control-Allow-Origin", origin)
            w.Header().Set("Access-Control-Allow-Credentials", "true")
            w.Header().Set("Access-Control-Allow-Headers", "Content-Type,X-CSRF-Token,X-Requested-With")
        }
        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusOK)
            return
        }
        next.ServeHTTP(w, r)
    })
}

// Wrap your router with the CORS middleware
http.ListenAndServe(":8080", corsMiddleware(r))

Example 4: Anti-CSRF token for browser clients

Generate and validate per-session or per-request tokens for browser-originated requests. This pattern is effective when Basic Auth is used but the client is a web application.

var csrfTokens = make(map[string]string)

func generateCSRFToken(sessionID string) string {
    token := fmt.Sprintf("%x", sha256.Sum256([]byte(sessionID+time.Now().String())))
    csrfTokens[sessionID] = token
    return token
}

func csrfMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        sessionID := r.Header.Get("X-Session-ID")
        if sessionID == "" {
            http.Error(w, "session required", http.StatusBadRequest)
            return
        }
        token := csrfTokens[sessionID]
        if token == "" {
            http.Error(w, "invalid session", http.StatusForbidden)
            return
        }
        if r.Header.Get("X-CSRF-Token") != token {
            http.Error(w, "invalid csrf token", http.StatusForbidden)
            return
        }
        next.ServeHTTP(w, r)
    })
}

// Usage: after validating Basic Auth, issue a session and token, then require X-CSRF-Token for state-changing methods.

Summary of concrete fixes

  • Require a custom header (e.g., X-CSRF-Token or X-Requested-With) on PUT/POST/DELETE routes in Gorilla Mux.
  • Validate Origin and avoid Access-Control-Allow-Credentials: true for untrusted origins.
  • Use per-session or per-request CSRF tokens for browser-based clients, even when Basic Auth is used.
  • Keep safe methods (GET) unrestricted where appropriate, and apply stricter checks only to state-changing methods.

Frequently Asked Questions

Does Gorilla Mux provide built-in CSRF protection when using Basic Auth?
No. Gorilla Mux is a routing library and does not include CSRF defenses. You must implement anti-CSRF measures such as custom headers, CSRF tokens, and strict CORS policies yourself.
Is using Basic Auth sufficient to prevent CSRF in browser-based applications?
No. Basic Auth headers can be automatically included by the browser in cross-origin requests if CORS allows credentials. This makes endpoints relying solely on Basic Auth vulnerable to CSRF unless additional protections (custom headers or tokens) are enforced.