### Install Gorilla CSRF Source: https://github.com/gorilla/csrf/blob/main/_autodocs/INDEX.md Use 'go get' to install the Gorilla CSRF package. ```bash go get github.com/gorilla/csrf ``` -------------------------------- ### Minimal CSRF Protection Example Source: https://github.com/gorilla/csrf/blob/main/_autodocs/usage-guide.md Demonstrates a basic setup for CSRF protection using Gorilla CSRF middleware with mux router. Ensure you use a strong, 32-byte secret key in production. ```go package main import ( "net/http" "github.com/gorilla/csrf" "github.com/gorilla/mux" ) func main() { r := mux.NewRouter() r.HandleFunc("/form", showForm).Methods("GET") r.HandleFunc("/form", submitForm).Methods("POST") // Create CSRF middleware with 32-byte auth key csrfMiddleware := csrf.Protect([]byte("your-32-byte-secret-key---------")) // Apply to router and start server http.ListenAndServe(":8000", csrfMiddleware(r)) } func showForm(w http.ResponseWriter, r *http.Request) { // Get CSRF token and embed in form w.Header().Set("Content-Type", "text/html") w.Write([]byte( "
")) } func submitForm(w http.ResponseWriter, r *http.Request) { // Token has been validated by middleware w.WriteHeader(http.StatusOK) w.Write([]byte("Form submitted successfully")) } ``` -------------------------------- ### CSRF Setup for HTML Forms with Templates Source: https://github.com/gorilla/csrf/blob/main/_autodocs/usage-guide.md Initializes the router and applies CSRF protection middleware. Replace the placeholder key with a secure secret. ```go import ( "html/template" "net/http" "github.com/gorilla/csrf" "github.com/gorilla/mux" ) func main() { r := mux.NewRouter() r.HandleFunc("/signup", showSignup).Methods("GET") r.HandleFunc("/signup", submitSignup).Methods("POST") http.ListenAndServe(":8000", csrf.Protect([]byte("32-byte-key-here"))(r)) } ``` -------------------------------- ### HTML Form CSRF Protection Example Source: https://github.com/gorilla/csrf/blob/main/README.md This example demonstrates how to protect HTML forms using the gorilla/csrf middleware. It includes setting up routes, applying the middleware, and injecting the CSRF token into the HTML template. ```go package main import ( "net/http" "github.com/gorilla/csrf" "github.com/gorilla/mux" ) func main() { r := mux.NewRouter() r.HandleFunc("/signup", ShowSignupForm) // All POST requests without a valid token will return HTTP 403 Forbidden. // We should also ensure that our mutating (non-idempotent) handler only // matches on POST requests. We can check that here, at the router level, or // within the handler itself via r.Method. r.HandleFunc("/signup/post", SubmitSignupForm).Methods("POST") // Add the middleware to your router by wrapping it. http.ListenAndServe(":8000", csrf.Protect([]byte("32-byte-long-auth-key"))(r)) // PS: Don't forget to pass csrf.Secure(false) if you're developing locally // over plain HTTP (just don't leave it on in production). } func ShowSignupForm(w http.ResponseWriter, r *http.Request) { // signup_form.tmpl just needs a {{ .csrfField }} template tag for // csrf.TemplateField to inject the CSRF token into. Easy! t.ExecuteTemplate(w, "signup_form.tmpl", map[string]interface{}{ csrf.TemplateTag: csrf.TemplateField(r), }) // We could also retrieve the token directly from csrf.Token(r) and // set it in the request header - w.Header.Set("X-CSRF-Token", token) // This is useful if you're sending JSON to clients or a front-end JavaScript // framework. } func SubmitSignupForm(w http.ResponseWriter, r *http.Request) { // We can trust that requests making it this far have satisfied // our CSRF protection requirements. } ``` -------------------------------- ### Minimal CSRF Setup Source: https://github.com/gorilla/csrf/blob/main/_autodocs/INDEX.md Apply the CSRF middleware to your router and retrieve tokens within handlers. Ensure the secret key is 32 bytes long. ```go // Apply middleware to router http.ListenAndServe(":8000", csrf.Protect([]byte("32-byte-secret-key"))(router)) // In handlers token := csrf.Token(r) // Get token field := csrf.TemplateField(r) // Get HTML input field ``` -------------------------------- ### Go Server Setup for CSRF Protection Source: https://github.com/gorilla/csrf/blob/main/_autodocs/usage-guide.md Configure a Go HTTP server with CSRF protection. This setup uses `mux.NewRouter` and `csrf.Protect` middleware to handle CSRF tokens, expecting them in the `X-CSRF-Token` request header. ```go func main() { r := mux.NewRouter() // GET: returns token in header r.HandleFunc("/api/data", getHandler).Methods("GET") // POST: validates token from request header r.HandleFunc("/api/data", postHandler).Methods("POST") http.ListenAndServe(":8000", csrf.Protect([]byte("32-byte-key"), csrf.RequestHeader("X-CSRF-Token"))(r)) ) } func getHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-CSRF-Token", csrf.Token(r)) w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"data":"example"}`)) } func postHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"status":"success"}`)) } ``` -------------------------------- ### Development Setup with PlaintextHTTPRequest Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/PlaintextHTTPRequest.md Use this configuration for local development with HTTP. It allows CSRF cookies over HTTP and marks requests as plaintext to bypass strict Referer validation. ```go csrf.Protect( []byte("key"), csrf.Secure(false), // Allow HTTP cookies )(plaintextMiddleware) // Mark requests as plaintext for Referer validation ``` -------------------------------- ### Initialize SecureCookie for Key Rotation Source: https://github.com/gorilla/csrf/blob/main/vendor/github.com/gorilla/securecookie/README.md Set up multiple SecureCookie instances for key rotation. This example uses a map to store 'previous' and 'current' keys, generated randomly. Note that these keys are not persisted between application restarts. ```go // keys stored in a map will not be persisted between restarts // a more persistent storage should be considered for production applications. var cookies = map[string]*securecookie.SecureCookie{ "previous": securecookie.New( securecookie.GenerateRandomKey(64), securecookie.GenerateRandomKey(32), ), "current": securecookie.New( securecookie.GenerateRandomKey(64), securecookie.GenerateRandomKey(32), ), } ``` -------------------------------- ### Setup CSRF for Cross-Domain APIs Source: https://github.com/gorilla/csrf/blob/main/_autodocs/usage-guide.md Configure CSRF protection to allow requests from trusted frontend domains. Ensure your frontend includes the CSRF token in the request headers. ```go func main() { r := mux.NewRouter() r.HandleFunc("/api/endpoint", apiHandler).Methods("POST") // Allow requests from trusted frontend domains csrfMiddleware := csrf.Protect( []byte("32-byte-key"), csrf.TrustedOrigins([]string{ "app.example.com", "staging-app.example.com", }), ) http.ListenAndServe(":8000", csrfMiddleware(r)) } func apiHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"success":true}`)) } ``` ```javascript // From app.example.com, making request to api.example.com fetch('https://api.example.com/api/endpoint', { method: 'POST', credentials: 'include', // Include cookies headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken, // Token from Referer check }, body: JSON.stringify({ data: 'value' }), }) ``` -------------------------------- ### Configure CSRF for Multi-Subdomain Site in Go Source: https://github.com/gorilla/csrf/blob/main/_autodocs/usage-guide.md Apply CSRF protection across all subdomains of a given domain. This setup ensures that cookies are correctly sent and validated for all subdomains. ```go func main() { r := mux.NewRouter() r.HandleFunc("/", homepage).Methods("GET") r.HandleFunc("/form", submitForm).Methods("POST") // Apply CSRF to all subdomains of example.com csrfMiddleware := csrf.Protect( []byte("32-byte-key"), csrf.Domain("example.com"), // Matches *.example.com csrf.Path("/"), // All paths ) http.ListenAndServe(":8000", csrfMiddleware(r)) } ``` -------------------------------- ### Combining Bearer Token and CSRF Protection Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/UnsafeSkipCheck.md This example demonstrates how to apply CSRF protection to web routes while using UnsafeSkipCheck to bypass it for API routes authenticated with bearer tokens. This allows for a mixed security approach. ```go import ( "net/http" "strings" "github.com/gorilla/csrf" "github.com/gorilla/mux" ) func main() { r := mux.NewRouter() // Web routes with CSRF (form submissions from browsers) r.HandleFunc("/form", showForm).Methods("GET") r.HandleFunc("/form", submitForm).Methods("POST") // API routes with bearer token authentication api := r.PathPrefix("/api").Subrouter() api.Use(csrf.Protect([]byte("32-byte-long-auth-key"))) api.Use(verifyAuthToken) api.HandleFunc("/endpoint", apiEndpoint).Methods("POST") http.ListenAndServe(":8000", r) } func verifyAuthToken(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { token := r.Header.Get("X-Auth-Token") if !isValidToken(token) { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } // Token-based auth protects this request r = csrf.UnsafeSkipCheck(r) next.ServeHTTP(w, r) }) } func apiEndpoint(w http.ResponseWriter, r *http.Request) { w.Write([]byte("success")) } ``` -------------------------------- ### Setup CSRF Middleware with Mux Router Source: https://github.com/gorilla/csrf/blob/main/README.md Configure CSRF protection for API sub-routers using gorilla/mux. This middleware should be applied to routes that handle sensitive state-changing operations. ```go package main import ( "github.com/gorilla/csrf" "github.com/gorilla/mux" ) func main() { r := mux.NewRouter() csrfMiddleware := csrf.Protect([]byte("32-byte-long-auth-key")) api := r.PathPrefix("/api").Subrouter() api.Use(csrfMiddleware) api.HandleFunc("/user/{id}", GetUser).Methods("GET") http.ListenAndServe(":8000", r) } func GetUser(w http.ResponseWriter, r *http.Request) { // Authenticate the request, get the id from the route params, // and fetch the user from the DB, etc. // Get the token and pass it in the CSRF header. Our JSON-speaking client // or JavaScript framework can now read the header and return the token in // in its own "X-CSRF-Token" request header on the subsequent POST. w.Header().Set("X-CSRF-Token", csrf.Token(r)) b, err := json.Marshal(user) if err != nil { http.Error(w, err.Error(), 500) return } w.Write(b) } ``` -------------------------------- ### Monitoring and Alerting Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/FailureReason.md Example of integrating CSRF failure reason into a monitoring system, allowing for metrics collection and alerting based on specific failure types. ```APIDOC ## Monitoring and Alerting ### Description This example demonstrates how to use `csrf.FailureReason` to capture CSRF validation failures and feed this information into a monitoring system. It shows how to categorize failures by type for metrics collection and potential alerting. ### Usage ```go import ( "fmt" "net/http" "github.com/gorilla/csrf" ) func monitoringErrorHandler(w http.ResponseWriter, r *http.Request) { reason := csrf.FailureReason(r) // Example: increment metrics counter failureType := "unknown" if reason != nil { failureType = reason.Error() } // Replace with your actual metrics reporting logic // metrics.Increment("csrf_failures", map[string]string{ // "type": failureType, // "path": r.URL.Path, // }) http.Error(w, "Forbidden", http.StatusForbidden) } ``` ``` -------------------------------- ### Custom Error Handler Configuration Source: https://github.com/gorilla/csrf/blob/main/_autodocs/configuration.md Implement custom error responses for CSRF failures. This example logs the failure reason and returns a 'Forbidden' status. ```go logErrorHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { reason := csrf.FailureReason(r) log.Printf("CSRF failed: %v from %s", reason, r.RemoteAddr) http.Error(w, "Forbidden", http.StatusForbidden) }) csrf.Protect( []byte("key-32-bytes-long--------------"), csrf.ErrorHandler(logErrorHandler), ) ``` -------------------------------- ### Logging with Request Details Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/FailureReason.md Example of logging CSRF failures along with relevant request details such as HTTP method, path, and remote address, using FailureReason to capture the error. ```APIDOC ## Logging with Request Details ### Description This example enhances CSRF failure logging by including contextual information from the `http.Request` object, such as the HTTP method, requested path, and the client's remote address, in addition to the specific failure reason obtained via `csrf.FailureReason`. ### Usage ```go import ( "log" "net/http" "github.com/gorilla/csrf" ) func detailedErrorHandler(w http.ResponseWriter, r *http.Request) { reason := csrf.FailureReason(r) log.Printf( "CSRF failure - Method: %s, Path: %s, RemoteAddr: %s, Reason: %v", r.Method, r.URL.Path, r.RemoteAddr, reason, ) http.Error(w, "Forbidden", http.StatusForbidden) } ``` ``` -------------------------------- ### Protect Specific Routes with CSRF Source: https://github.com/gorilla/csrf/blob/main/_autodocs/usage-guide.md This example demonstrates how to apply CSRF protection selectively to different route groups. Web routes and API endpoints requiring CSRF are protected, while public routes are excluded. It also shows how CSRF can be bypassed for API routes if token authentication is handled separately. ```go func main() { r := mux.NewRouter() // Web routes with CSRF protection webRouter := r.PathPrefix("/web").Subrouter() webRouter.Use(csrf.Protect([]byte("32-byte-key"))) webRouter.HandleFunc("/form", webFormHandler).Methods("POST") // Public routes (no CSRF needed) r.HandleFunc("/health", healthHandler).Methods("GET") // API routes with token auth (CSRF bypassed via bearer token) apiRouter := r.PathPrefix("/api").Subrouter() apiRouter.Use(requireBearerToken) apiRouter.Use(csrf.Protect([]byte("32-byte-key"))) apiRouter.HandleFunc("/endpoint", apiHandler).Methods("POST") http.ListenAndServe(":8000", r) } ``` -------------------------------- ### JSON Error Response with Reason Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/FailureReason.md Example of creating a JSON response for CSRF failures, including the specific failure reason obtained via FailureReason. ```APIDOC ## JSON Error Response with Reason ### Description This example shows how to construct a JSON response when a CSRF validation fails. It uses `csrf.FailureReason` to get the error message and includes it in the JSON payload. ### Usage ```go import ( "encoding/json" "net/http" "github.com/gorilla/csrf" ) func jsonErrorHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusForbidden) errorMsg := "CSRF validation failed" reason := csrf.FailureReason(r) if reason != nil { errorMsg = reason.Error() } json.NewEncoder(w).Encode(map[string]string{ "error": errorMsg, }) } ``` ``` -------------------------------- ### Get CSRF Options Based on Environment Source: https://github.com/gorilla/csrf/blob/main/_autodocs/configuration.md This function retrieves CSRF options, applying secure and path settings for production environments. It also parses trusted origins from the CSRF_TRUSTED_ORIGINS environment variable. ```go func getCsrfOptions() []csrf.Option { opts := []csrf.Option{ csrf.MaxAge(3600), } if os.Getenv("ENV") == "production" { opts = append(opts, csrf.Secure(true)) opts = append(opts, csrf.Path("/")) } else { opts = append(opts, csrf.Secure(false)) } if trustedOrigins := os.Getenv("CSRF_TRUSTED_ORIGINS"); trustedOrigins != "" { origins := strings.Split(trustedOrigins, ",") opts = append(opts, csrf.TrustedOrigins(origins)) } return opts } // Usage csrf.Protect(key, getCsrfOptions()...) ``` -------------------------------- ### Unit Test CSRF Middleware with HTTP Requests Source: https://github.com/gorilla/csrf/blob/main/_autodocs/usage-guide.md This Go test demonstrates how to unit test handlers protected by CSRF middleware. It simulates a GET request to obtain a CSRF token and then a POST request with the token to verify protection. ```go import ( "net/http" "net/http/httptest" "testing" "github.com/gorilla/csrf" ) func TestFormSubmission(t *testing.T) { // Handler handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { token := csrf.Token(r) w.Write([]byte(token)) } else { w.WriteHeader(http.StatusOK) } }) // CSRF middleware csrfProtected := csrf.Protect( []byte("test-key-32-bytes-long-----------"), csrf.Secure(false), // Disable HTTPS requirement for tests )(handler) // Step 1: GET to retrieve token getReq := httptest.NewRequest("GET", "/form", nil) getReq = csrf.PlaintextHTTPRequest(getReq) recorder := httptest.NewRecorder() csrfProtected.ServeHTTP(recorder, getReq) token := recorder.Body.String() if token == "" { t.Fatal("No token returned") } // Step 2: POST with token postReq := httptest.NewRequest("POST", "/form", nil) postReq = csrf.PlaintextHTTPRequest(postReq) postReq.Header.Set("X-CSRF-Token", token) postReq.Header.Set("Cookie", recorder.Header().Get("Set-Cookie")) recorder = httptest.NewRecorder() csrfProtected.ServeHTTP(recorder, postReq) if recorder.Code != http.StatusOK { t.Fatalf("POST failed: got %d, want 200", recorder.Code) } } ``` -------------------------------- ### Increase CSRF Token Expiration Source: https://github.com/gorilla/csrf/blob/main/_autodocs/usage-guide.md If tokens expire too quickly, increase the `MaxAge` duration. This example sets it to 24 hours. ```go csrf.Protect(key, csrf.MaxAge(86400)) // 24 hours ``` -------------------------------- ### Initialize SecureCookie Instance Source: https://github.com/gorilla/csrf/blob/main/vendor/github.com/gorilla/securecookie/README.md Create a new SecureCookie instance with hash and block keys. The hash key is required for authentication, and the block key is optional for encryption. Ensure keys are of appropriate lengths for security. ```go // Hash keys should be at least 32 bytes long var hashKey = []byte("very-secret") // Block keys should be 16 bytes (AES-128) or 32 bytes (AES-256) long. // Shorter keys may weaken the encryption used. var blockKey = []byte("a-lot-secret") var s = securecookie.New(hashKey, blockKey) ``` -------------------------------- ### CSRF Protection with SameSite Modes Source: https://github.com/gorilla/csrf/blob/main/_autodocs/types.md Demonstrates how to apply different SameSiteMode configurations when protecting routes with CSRF. ```go csrf.Protect(key, csrf.SameSite(csrf.SameSiteLaxMode)) ``` ```go csrf.Protect(key, csrf.SameSite(csrf.SameSiteStrictMode)) ``` ```go csrf.Protect(key, csrf.SameSite(csrf.SameSiteNoneMode)) ``` -------------------------------- ### CSRF Middleware Configuration with Options Source: https://github.com/gorilla/csrf/blob/main/_autodocs/types.md Shows how to configure CSRF middleware using single or multiple functional options. ```go import ( "net/http" "github.com/gorilla/csrf" ) // Single option middleware := csrf.Protect(key, csrf.Secure(false)) // Multiple options middleware := csrf.Protect( key, csrf.Secure(false), csrf.MaxAge(3600), csrf.Path("/"), csrf.SameSite(csrf.SameSiteLaxMode), ) // Apply to handler http.ListenAndServe(":8000", middleware(router)) ``` -------------------------------- ### CSRF Handler Implementation for Signup Form Source: https://github.com/gorilla/csrf/blob/main/_autodocs/usage-guide.md Renders the signup form, injecting the CSRF token into the template context. The middleware automatically validates the token on POST requests. ```go var formTmpl = template.Must(template.ParseFiles("signup.html")) func showSignup(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{ csrf.TemplateTag: csrf.TemplateField(r), } formTmpl.Execute(w, data) } func submitSignup(w http.ResponseWriter, r *http.Request) { username := r.FormValue("username") email := r.FormValue("email") // Token already validated by middleware // Process form submission... w.WriteHeader(http.StatusOK) w.Write([]byte("Welcome, " + username)) } ``` -------------------------------- ### Get CSRF Token in JSON API Responses Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/Token.md Retrieve the CSRF token and include it in a JSON API response. This is useful for single-page applications that manage their own forms. ```go import ( "encoding/json" "net/http" "github.com/gorilla/csrf" ) func getCSRFToken(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") response := map[string]string{ "token": csrf.Token(r), } json.NewEncoder(w).Encode(response) } ``` -------------------------------- ### Get CSRF Token in HTML Templates Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/Token.md Retrieve the CSRF token and pass it to an HTML template for embedding in a form. Ensure the CSRF middleware is applied to the handler. ```go import ( "html/template" "net/http" "github.com/gorilla/csrf" ) func renderForm(w http.ResponseWriter, r *http.Request) { // Get the CSRF token and pass it to the template token := csrf.Token(r) htmlTemplate := ` ` t := template.Must(template.New("form").Parse(htmlTemplate)) t.Execute(w, map[string]string{"Token": token}) } ``` -------------------------------- ### Basic CSRF Test Pattern in Go Source: https://github.com/gorilla/csrf/blob/main/_autodocs/INDEX.md This snippet demonstrates a basic test pattern for the CSRF middleware. It covers obtaining a token and then using it in a subsequent POST request. Ensure the middleware is configured with a valid key and appropriate options for your environment. ```go func TestCSRF(t *testing.T) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { w.Write([]byte(csrf.Token(r))) } else { w.WriteHeader(http.StatusOK) } }) csrfMiddleware := csrf.Protect( []byte("test-key-32-bytes-long-----------"), csrf.Secure(false), )(handler) // Get token req := httptest.NewRequest("GET", "/", nil) req = csrf.PlaintextHTTPRequest(req) recorder := httptest.NewRecorder() csrfMiddleware.ServeHTTP(recorder, req) token := recorder.Body.String() // Use token req = httptest.NewRequest("POST", "/", nil) req = csrf.PlaintextHTTPRequest(req) req.Header.Set("X-CSRF-Token", token) req.Header.Set("Cookie", recorder.Header().Get("Set-Cookie")) recorder = httptest.NewRecorder() csrfMiddleware.ServeHTTP(recorder, req) if recorder.Code != http.StatusOK { t.Fatalf("Expected 200, got %d", recorder.Code) } } ``` -------------------------------- ### SameSiteMode Constants Source: https://github.com/gorilla/csrf/blob/main/_autodocs/types.md Defines constants for SameSiteMode, including Default, Lax, Strict, and None modes. ```go const ( SameSiteDefaultMode SameSiteMode = iota + 1 SameSiteLaxMode SameSiteStrictMode SameSiteNoneMode ) ``` -------------------------------- ### Multi-Subdomain Site Configuration Source: https://github.com/gorilla/csrf/blob/main/_autodocs/configuration.md Configure CSRF protection for sites spanning multiple subdomains. This ensures consistent protection across *.example.com. ```go csrf.Protect( []byte("domain-key-32-bytes-long-------"), csrf.Domain("example.com"), csrf.Path("/"), csrf.MaxAge(86400), ) ``` -------------------------------- ### Development Configuration for HTTP Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/Protect.md Configures CSRF protection for development environments on localhost, allowing HTTP traffic by disabling the Secure option. ```go // For development on localhost with HTTP csrf.Protect( []byte("32-byte-long-auth-key"), csrf.Secure(false), // Allow HTTP csrf.Domain("localhost"), ) ``` -------------------------------- ### Logging CSRF Failures Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/FailureReason.md Example of how to use FailureReason within a custom error handler to log the specific CSRF validation failure reason before returning a generic forbidden error. ```APIDOC ## Logging CSRF Failures ### Description This example demonstrates how to capture the CSRF validation failure reason using `csrf.FailureReason` and log it using Go's standard `log` package within a custom HTTP error handler. ### Usage ```go import ( "log" "net/http" "github.com/gorilla/csrf" ) func customErrorHandler(w http.ResponseWriter, r *http.Request) { reason := csrf.FailureReason(r) if reason != nil { log.Printf("CSRF validation failed: %v", reason) } http.Error(w, "Forbidden", http.StatusForbidden) } func main() { // ... setup csrf.Protect with customErrorHandler ... // csrf.Protect( // []byte("32-byte-long-auth-key"), // csrf.ErrorHandler(http.HandlerFunc(customErrorHandler)), // ) } ``` ``` -------------------------------- ### CSRF Protection for Production (HTTPS) Source: https://github.com/gorilla/csrf/blob/main/_autodocs/usage-guide.md This configuration is recommended for production environments using HTTPS. It loads the key from an environment variable, enforces HTTPS, sets HttpOnly, uses strict SameSite policy, and sets a longer expiration. ```go csrf.Protect( os.Getenv("CSRF_KEY"), // Load from env var csrf.Secure(true), // HTTPS only csrf.HttpOnly(true), // No JS access csrf.SameSite(csrf.SameSiteStrictMode), // Strict CSRF csrf.MaxAge(3600), // 1 hour csrf.Path("/"), // All paths ) ``` -------------------------------- ### CSRF Protection for Development Source: https://github.com/gorilla/csrf/blob/main/_autodocs/usage-guide.md Use this configuration for development environments. It allows HTTP and sets a short token expiration. ```go csrf.Protect( []byte("dev-key"), csrf.Secure(false), // Allow HTTP csrf.MaxAge(300), // 5 minutes (short for iteration) ) ``` -------------------------------- ### Basic CSRF Configuration Source: https://github.com/gorilla/csrf/blob/main/_autodocs/INDEX.md Configure CSRF protection with options like Secure (for development over HTTP), MaxAge for cookie expiration, and SameSite policy. ```go csrf.Protect( []byte("32-byte-auth-key"), csrf.Secure(false), // Dev: allow HTTP csrf.MaxAge(3600), // 1 hour csrf.SameSite(csrf.SameSiteLaxMode), // Cross-site safety ) ``` -------------------------------- ### Configure CSRF for Production Environment Source: https://github.com/gorilla/csrf/blob/main/_autodocs/INDEX.md Configure CSRF protection for production with secure settings: enable secure flag, set HttpOnly cookies, define the cookie path, enforce strict SameSite mode, and set a reasonable token expiration. ```go opts := []csrf.Option{ csrf.Secure(true), csrf.HttpOnly(true), csrf.Path("/"), csrf.SameSite(csrf.SameSiteStrictMode), csrf.MaxAge(3600), } csrf.Protect(prodKey, opts...) ``` -------------------------------- ### Load Balancer Scenario Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/PlaintextHTTPRequest.md Trust the X-Forwarded-Proto header from a load balancer to determine if the original request was HTTPS. If not, mark the request as plaintext. ```go import ( "net/http" "strings" "github.com/gorilla/csrf" "github.com/gorilla/mux" ) func main() { r := mux.NewRouter() r.HandleFunc("/api", handleAPI).Methods("POST") // Trust X-Forwarded-Proto header from load balancer proxyMiddleware := func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // If load balancer indicates original was HTTPS, don't mark as plaintext forwardedProto := r.Header.Get("X-Forwarded-Proto") if forwardedProto != "https" && r.Header.Get("X-Forwarded-Proto") == "" { // No indication of HTTPS, mark as plaintext to relax validation r = csrf.PlaintextHTTPRequest(r) } next.ServeHTTP(w, r) }) } http.ListenAndServe(":8000", proxyMiddleware(csrf.Protect([]byte("32-byte-long-auth-key"))(r))) } ``` -------------------------------- ### Configure CSRF for Development vs. Production Source: https://github.com/gorilla/csrf/blob/main/_autodocs/usage-guide.md This snippet shows how to conditionally configure CSRF options based on the environment. It allows HTTP and shorter timeouts in development for easier iteration, while enforcing HTTPS and strict SameSite policies in production. ```go import ( "os" "github.com/gorilla/csrf" ) func getCsrfConfig() []csrf.Option { opts := []csrf.Option{ csrf.MaxAge(3600), // 1 hour } // Development: allow HTTP, shorter timeout if os.Getenv("ENV") != "production" { opts = append(opts, csrf.Secure(false), csrf.MaxAge(300), // 5 minutes for quick iteration ) } else { // Production: HTTPS only, strict CSRF opts = append(opts, csrf.Secure(true), csrf.Path("/"), csrf.SameSite(csrf.SameSiteStrictMode), ) } return opts } func main() { r := mux.NewRouter() // ... routes ... http.ListenAndServe(":8000", csrf.Protect([]byte("key"), getCsrfConfig()...)(r)) } ``` -------------------------------- ### Basic HTML Form Protection with Gorilla CSRF Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/Protect.md Applies CSRF protection to an entire router and demonstrates how to include the CSRF token in an HTML form for POST requests. ```go import ( "net/http" "github.com/gorilla/csrf" "github.com/gorilla/mux" ) func main() { r := mux.NewRouter() r.HandleFunc("/form", showForm).Methods("GET") r.HandleFunc("/form", submitForm).Methods("POST") // Apply CSRF protection to the entire router http.ListenAndServe(":8000", csrf.Protect([]byte("32-byte-long-auth-key"))(r)) } func showForm(w http.ResponseWriter, r *http.Request) { token := csrf.Token(r) // Render form with token field w.Write([]byte(``)) } func submitForm(w http.ResponseWriter, r *http.Request) { // Token has been validated by the middleware w.WriteHeader(http.StatusOK) w.Write([]byte("Form submitted successfully")) } ``` -------------------------------- ### Local Development HTTP Configuration Source: https://github.com/gorilla/csrf/blob/main/_autodocs/configuration.md Configure CSRF protection for local development to allow HTTP connections. This is useful for testing without HTTPS. ```go csrf.Protect( []byte("dev-key-32-bytes-long-----------"), csrf.Secure(false), csrf.Path("/"), ) ``` -------------------------------- ### Configure CSRF for Development Environment Source: https://github.com/gorilla/csrf/blob/main/_autodocs/INDEX.md Configure CSRF protection for development by disabling the secure flag (allowing HTTP) and setting a short maximum age for tokens to facilitate rapid iteration. ```go opts := []csrf.Option{ csrf.Secure(false), // Allow HTTP csrf.MaxAge(300), // Short timeout for iteration } csrf.Protect(devKey, opts...) ``` -------------------------------- ### Composing CSRF Middleware with Other Middleware Source: https://github.com/gorilla/csrf/blob/main/_autodocs/architecture.md Demonstrates how to integrate the CSRF middleware into a chain of other HTTP middleware handlers. Ensure the CSRF middleware is placed appropriately within the stack to protect the intended routes. ```go http.ListenAndServe(":8000", loggingMiddleware( csrfMiddleware( authMiddleware( router)))) ``` -------------------------------- ### Localhost Development Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/PlaintextHTTPRequest.md Allow plaintext HTTP on localhost or 127.0.0.1 by marking requests as plaintext. This is useful for local development without HTTPS. ```go import ( "net/http" "strings" "github.com/gorilla/csrf" "github.com/gorilla/mux" ) func main() { r := mux.NewRouter() r.HandleFunc("/", handleRequest) // Allow plaintext HTTP on localhost only localhostMiddleware := func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Check if this is a local request if strings.Contains(r.Host, "localhost") || strings.Contains(r.Host, "127.0.0.1") { r = csrf.PlaintextHTTPRequest(r) } next.ServeHTTP(w, r) }) } http.ListenAndServe(":8000", localhostMiddleware(csrf.Protect([]byte("32-byte-long-auth-key"))(r))) } ``` -------------------------------- ### Production HTTPS API Configuration Source: https://github.com/gorilla/csrf/blob/main/_autodocs/configuration.md Use this configuration for production environments requiring HTTPS. It enforces secure, HttpOnly, and SameSite cookies with a 1-hour max age. ```go csrf.Protect( []byte("production-key-32-bytes-long----"), csrf.Secure(true), csrf.HttpOnly(true), csrf.SameSite(csrf.SameSiteLaxMode), csrf.Path("/"), csrf.MaxAge(3600), ) ``` -------------------------------- ### Basic CSRF Middleware Integration Source: https://github.com/gorilla/csrf/blob/main/README.md Integrate the CSRF middleware into your router by wrapping it with csrf.Protect. The authentication key must be 32 bytes long, persist across restarts, and be kept secret. ```go CSRF := csrf.Protect([]byte("32-byte-long-auth-key")) http.ListenAndServe(":8000", CSRF(r)) ``` -------------------------------- ### Context Key Type Definition Source: https://github.com/gorilla/csrf/blob/main/_autodocs/types.md Defines the type for context keys used within the package. ```go type contextKey string ``` -------------------------------- ### CSRF Error: No Referer Source: https://github.com/gorilla/csrf/blob/main/_autodocs/types.md Returned when an HTTPS request is missing the required Referer header. ```go var ErrNoReferer = errors.New("referer not supplied") ``` -------------------------------- ### Selective CSRF Protection with Mux Router Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/UnsafeSkipCheck.md Shows how to apply CSRF protection selectively to specific routes using `mux.Router`. Routes under `/web` have protection, while routes under `/api` do not. ```go r := mux.NewRouter() // Routes with CSRF protection webRouter := r.PathPrefix("/web").Subrouter() webRouter.Use(csrf.Protect([]byte("32-byte-long-auth-key"))) webRouter.HandleFunc("/form", handleForm) // Routes without CSRF (API with bearer tokens) apiRouter := r.PathPrefix("/api").Subrouter() apiRouter.HandleFunc("/endpoint", handleAPI) http.ListenAndServe(":8000", r) ``` -------------------------------- ### UnsafeSkipCheck Usage Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/UnsafeSkipCheck.md Demonstrates the incorrect and correct ways to use UnsafeSkipCheck. Call it before the CSRF middleware, typically within another middleware that runs earlier. ```go r = csrf.UnsafeSkipCheck(r) ``` ```go func myMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if isAPIRequest(r) { r = csrf.UnsafeSkipCheck(r) } next.ServeHTTP(w, r) }) } // Then apply middlewares in this order: csrf.Protect(authKey)(myMiddleware(router)) ``` -------------------------------- ### Cross-Domain API Configuration Source: https://github.com/gorilla/csrf/blob/main/_autodocs/configuration.md Use this configuration for cross-domain APIs where the frontend is hosted on a different domain. It specifies the request header and trusted origins. ```go csrf.Protect( []byte("api-key-32-bytes-long----------"), csrf.RequestHeader("X-CSRF-Token"), csrf.TrustedOrigins([]string{ "frontend.example.com", "app.example.com", }), ) ``` -------------------------------- ### Development Environment with HTTP Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/PlaintextHTTPRequest.md In development, mark requests as plaintext to relax CSRF validation when not using HTTPS. This is typically done via an environment variable check. ```go import ( "net/http" "os" "github.com/gorilla/csrf" "github.com/gorilla/mux" ) func main() { r := mux.NewRouter() r.HandleFunc("/form", showForm).Methods("GET") r.HandleFunc("/form", submitForm).Methods("POST") // In development, mark requests as plaintext localMiddleware := func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if os.Getenv("ENV") == "development" { r = csrf.PlaintextHTTPRequest(r) } next.ServeHTTP(w, r) }) } http.ListenAndServe(":8000", localMiddleware(csrf.Protect([]byte("32-byte-long-auth-key"))(r))) } ``` -------------------------------- ### API Key Authentication Middleware Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/UnsafeSkipCheck.md Implement API key authentication using a middleware that validates the 'X-API-Key' header. If valid, UnsafeSkipCheck is called to bypass CSRF protection for API requests. ```go import ( "net/http" "github.com/gorilla/csrf" ) func apiKeyMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { apiKey := r.Header.Get("X-API-Key") // Validate API key from database or configuration if !validateAPIKey(apiKey) { http.Error(w, "Forbidden", http.StatusForbidden) return } // API key provides authentication; skip CSRF r = csrf.UnsafeSkipCheck(r) next.ServeHTTP(w, r) }) } func validateAPIKey(key string) bool { // Check against known API keys validKeys := map[string]bool{ "key_1234567890": true, "key_0987654321": true, } return validKeys[key] } ``` -------------------------------- ### Apply CSRF Protection Middleware in Go Source: https://github.com/gorilla/csrf/blob/main/_autodocs/README.md Apply the CSRF protection middleware to your http.Handler. Ensure your authentication key is a 32-byte random string. ```go http.ListenAndServe(":8000", csrf.Protect([]byte("32-byte-auth-key"))(router)) ``` -------------------------------- ### Google App Engine Standard Environment CSRF Integration Source: https://github.com/gorilla/csrf/blob/main/README.md Integrate gorilla/csrf with Google App Engine's first-generation standard environment by wrapping the main router with the CSRF protection middleware before passing it to the DefaultServeMux. ```go package app // Remember: appengine has its own package main func init() { r := mux.NewRouter() r.HandleFunc("/", IndexHandler) // ... // We pass our CSRF-protected router to the DefaultServeMux http.Handle("/", csrf.Protect([]byte(your-key))(r)) } ``` -------------------------------- ### Protect Middleware Function Signature Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/Protect.md This is the function signature for the Protect middleware. It takes an authentication key and optional configuration options, returning a middleware handler. ```go func Protect(authKey []byte, opts ...Option) func(http.Handler) http.Handler ``` -------------------------------- ### CSRF Store Interface Definition Source: https://github.com/gorilla/csrf/blob/main/_autodocs/architecture.md Defines the `store` interface for abstracting token storage. Implementations can vary, allowing for different storage backends. ```go type store interface { Get(*http.Request) ([]byte, error) Save(token []byte, w http.ResponseWriter) error } ``` -------------------------------- ### Set CSRF Cookie SameSite Mode Source: https://github.com/gorilla/csrf/blob/main/_autodocs/configuration.md Configures the SameSite attribute for cross-site cookie behavior. Requires Go 1.11+. SameSiteNoneMode requires Secure(true). ```go csrf.Protect(key, csrf.SameSite(csrf.SameSiteStrictMode)) ``` ```go csrf.Protect(key, csrf.SameSite(csrf.SameSiteLaxMode)) ``` ```go csrf.Protect(key, csrf.SameSite(csrf.SameSiteNoneMode)) ``` -------------------------------- ### Testing Scenario Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/PlaintextHTTPRequest.md Mark requests as HTTP for local testing when CSRF Secure is disabled. This allows testing CSRF token generation and submission without requiring HTTPS. ```go import ( "net/http" "net/http/httptest" "testing" "github.com/gorilla/csrf" ) func TestFormSubmission(t *testing.T) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { token := csrf.Token(r) w.WriteHeader(http.StatusOK) w.Write([]byte(token)) } else if r.Method == "POST" { w.WriteHeader(http.StatusOK) } }) csrfProtected := csrf.Protect( []byte("32-byte-long-auth-key"), csrf.Secure(false), // Disable HTTPS requirement for tests )(handler) // Create a GET request to get the token getReq := httptest.NewRequest("GET", "/form", nil) getReq = csrf.PlaintextHTTPRequest(getReq) // Mark as HTTP for local test recorder := httptest.NewRecorder() csrfProtected.ServeHTTP(recorder, getReq) // Extract token from response and use in POST token := recorder.Body.String() postReq := httptest.NewRequest("POST", "/form", nil) postReq = csrf.PlaintextHTTPRequest(postReq) postReq.Header.Set("X-CSRF-Token", token) recorder = httptest.NewRecorder() csrfProtected.ServeHTTP(recorder, postReq) if recorder.Code != http.StatusOK { t.Fatalf("POST failed with status %d", recorder.Code) } } ``` -------------------------------- ### Handle File Uploads with CSRF Protection Source: https://github.com/gorilla/csrf/blob/main/_autodocs/usage-guide.md This Go code sets up HTTP routes for displaying an upload form and handling file uploads, with CSRF protection applied to the entire router. Ensure your HTML form includes the CSRF token field. ```go func main() { r := mux.NewRouter() r.HandleFunc("/upload", showUploadForm).Methods("GET") r.HandleFunc("/upload", handleUpload).Methods("POST") http.ListenAndServe(":8000", csrf.Protect([]byte("32-byte-key"))(r)) } var uploadFormTmpl = template.Must(template.ParseFiles("upload.html")) func showUploadForm(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{ csrf.TemplateTag: csrf.TemplateField(r), } uploadFormTmpl.Execute(w, data) } func handleUpload(w http.ResponseWriter, r *http.Request) { // Parse multipart form if err := r.ParseMultipartForm(10 << 20); err != nil { http.Error(w, "File too large", http.StatusBadRequest) return } // CSRF token in multipart form is automatically validated // Access form values file, header, err := r.FormFile("file") if err != nil { http.Error(w, "Error reading file", http.StatusBadRequest) return } defer file.Close() // Process file... w.WriteHeader(http.StatusOK) w.Write([]byte("File uploaded: " + header.Filename)) } ``` -------------------------------- ### Configure Custom Request Header and Error Handler Source: https://github.com/gorilla/csrf/blob/main/README.md Allows customization of the HTTP header inspected for CSRF tokens and provides a custom error handler for forbidden requests. ```go func main() { CSRF := csrf.Protect( []byte("a-32-byte-long-key-goes-here"), csrf.RequestHeader("Authenticity-Token"), csrf.FieldName("authenticity_token"), csrf.ErrorHandler(http.HandlerFunc(serverError(403))), ) r := mux.NewRouter() r.HandleFunc("/signup", GetSignupForm) r.HandleFunc("/signup/post", PostSignupForm) http.ListenAndServe(":8000", CSRF(r)) } ``` -------------------------------- ### Combining with Custom Attributes Source: https://github.com/gorilla/csrf/blob/main/_autodocs/api-reference/TemplateField.md Demonstrates how to manually construct the CSRF hidden input field using the CSRF token for more advanced customization, such as adding data attributes. ```APIDOC ## Combining with Custom Attributes ### Usage Example ```go import ( "fmt" "html/template" "net/http" "github.com/gorilla/csrf" ) func showFormWithDataAttributes(w http.ResponseWriter, r *http.Request) { token := csrf.Token(r) fieldName := "authenticity_token" // For more control, you can build the HTML manually htmlStr := fmt.Sprintf( ``, fieldName, token, ) w.Header().Set("Content-Type", "text/html") w.Write([]byte(htmlStr)) } ``` ``` -------------------------------- ### Handle ErrNoReferer in Go Source: https://github.com/gorilla/csrf/blob/main/_autodocs/errors.md Check if the Referer header is missing or invalid for an HTTPS request. This error is triggered when a non-safe HTTP method is used on an HTTPS connection, and neither an Origin header nor a valid Referer header is present. Log the potential attack and return a forbidden status. ```go reason := csrf.FailureReason(r) if errors.Is(reason, csrf.ErrNoReferer) { // Log potential attack log.Printf("CSRF: Missing Referer on HTTPS POST from %s", r.RemoteAddr) http.Error(w, "Forbidden", http.StatusForbidden) } ``` -------------------------------- ### Skip CSRF Check for Authenticated API Routes Source: https://github.com/gorilla/csrf/blob/main/_autodocs/INDEX.md Create a middleware to perform authentication before CSRF protection. Use `UnsafeSkipCheck` to bypass CSRF validation for routes where authentication is sufficient, such as internal APIs. ```go // Middleware that checks auth before CSRF func requireAuth(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !isAuthenticated(r) { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } r = csrf.UnsafeSkipCheck(r) // Auth validates request next.ServeHTTP(w, r) }) } api.Use(requireAuth) api.Use(csrf.Protect(key)) ``` -------------------------------- ### CSRF Error: Bad Referer Source: https://github.com/gorilla/csrf/blob/main/_autodocs/types.md Returned when the Referer header is invalid, either indicating an HTTP origin for an HTTPS request or a mismatched domain. ```go var ErrBadReferer = errors.New("referer invalid") ``` -------------------------------- ### CSRF Error: No Token Source: https://github.com/gorilla/csrf/blob/main/_autodocs/types.md Returned when the CSRF middleware expects a token but none is provided in the request headers, form body, or multipart data. ```go var ErrNoToken = errors.New("CSRF token not found in request") ``` -------------------------------- ### CSRF Protect Function Signature Source: https://github.com/gorilla/csrf/blob/main/_autodocs/architecture.md Defines the `Protect` function which initializes CSRF middleware using the functional options pattern. It accepts an authentication key and a variadic list of options. ```go func Protect(authKey []byte, opts ...Option) func(http.Handler) http.Handler { return func(h http.Handler) http.Handler { cs := parseOptions(h, opts...) // Apply defaults and create middleware return cs } } ```