CSRF Protection
Cross-Site Request Forgery (CSRF) is an attack that tricks users into performing unwanted actions on a web application where they’re authenticated. Fluxbase provides built-in CSRF protection to prevent these attacks.
What is CSRF?
Section titled “What is CSRF?”CSRF attacks exploit the trust a web application has in a user’s browser. If a user is logged into your application, an attacker can trick their browser into making requests to your application without their knowledge.
Example Attack Scenario
Section titled “Example Attack Scenario”- User logs into
https://yourapp.com - User visits malicious site
https://evil.com - Malicious site contains:
<img src="https://yourapp.com/api/v1/tables/users?delete=all" />
- Browser automatically includes authentication cookies
- Unintended action is performed
How Fluxbase Prevents CSRF
Section titled “How Fluxbase Prevents CSRF”Fluxbase implements the Double-Submit Cookie pattern:
- Server generates a random CSRF token
- Token is stored in:
- HTTP-only cookie (not accessible to JavaScript)
- Response header/body (for client to read)
- Client includes token in subsequent requests
- Server validates both tokens match
Request Flow
Section titled “Request Flow”Client Server | | |-- GET /api/v1/users --------->| | | Generate CSRF token |<------- Set-Cookie ----------| Set csrf_token cookie | | |-- POST /api/v1/users -------->| | X-CSRF-Token: abc123 | Validate: | Cookie: csrf_token=abc123 | - Cookie matches header | | - Token exists in storage |<------- 200 OK ---------------|Configuration
Section titled “Configuration”Enable CSRF Protection
Section titled “Enable CSRF Protection”CSRF protection is enabled by default for state-changing methods (POST, PUT, PATCH, DELETE).
CSRF protection uses built-in defaults. The double-submit cookie pattern, excluded paths, and token settings are preconfigured.
Client Implementation
Section titled “Client Implementation”Vanilla JavaScript/TypeScript
Section titled “Vanilla JavaScript/TypeScript”// 1. Get CSRF token from cookiefunction getCsrfToken(): string | null { const match = document.cookie.match(/csrf_token=([^;]+)/); return match ? match[1] : null;}
// 2. Include token in requestsasync function makeRequest(url: string, data: any) { const csrfToken = getCsrfToken();
const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", "X-CSRF-Token": csrfToken || "", // Include CSRF token Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify(data), credentials: "include", // Include cookies });
if (!response.ok) { throw new Error("Request failed"); }
return response.json();}
// Example usagemakeRequest("/api/v1/tables/users", { name: "John Doe", email: "john@example.com",});Fluxbase SDK (Automatic)
Section titled “Fluxbase SDK (Automatic)”The Fluxbase SDK handles CSRF tokens automatically:
import { createClient } from "@nimbleflux/fluxbase-sdk";
const client = createClient("http://localhost:8080", "your-anon-key");
// CSRF token is automatically includedawait client .from("users") .insert({ name: "John Doe", email: "john@example.com", }) .execute();React Example
Section titled “React Example”import { createClient } from "@nimbleflux/fluxbase-sdk";
const client = createClient("http://localhost:8080", "your-anon-key");
function UserForm() { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault();
// CSRF token is automatically included by the SDK const { error } = await client.from("users").insert({ name: "John Doe", email: "john@example.com", });
if (error) { console.error("Request failed:", error.message); } };
return ( <form onSubmit={handleSubmit}> {/* Form fields */} <button type="submit">Submit</button> </form> );}Vue.js Example
Section titled “Vue.js Example”<template> <form @submit.prevent="handleSubmit"> <!-- Form fields --> <button type="submit">Submit</button> </form></template>
<script>import { createClient } from "@nimbleflux/fluxbase-sdk";
const client = createClient("http://localhost:8080", "your-anon-key");
export default { methods: { async handleSubmit() { // CSRF token is automatically included by the SDK const { error } = await client.from("users").insert({ name: "John Doe", email: "john@example.com", });
if (error) { console.error("Request failed:", error.message); } }, },};</script>Axios Interceptor
Section titled “Axios Interceptor”import axios from "axios";
// Create Axios instanceconst api = axios.create({ baseURL: "http://localhost:8080", withCredentials: true, // Include cookies});
// Add CSRF token to all requestsapi.interceptors.request.use((config) => { const match = document.cookie.match(/csrf_token=([^;]+)/); const csrfToken = match ? match[1] : null;
if (csrfToken && config.headers) { config.headers["X-CSRF-Token"] = csrfToken; }
return config;});
// Usageapi.post("/api/v1/tables/users", { name: "John Doe", email: "john@example.com",});Server-Side Implementation
Section titled “Server-Side Implementation”Custom Middleware (Go)
Section titled “Custom Middleware (Go)”If you’re building custom endpoints, use the CSRF middleware:
package main
import ( "github.com/gofiber/fiber/v2" "github.com/nimbleflux/fluxbase/internal/middleware")
func main() { app := fiber.New()
// Apply CSRF middleware app.Use(middleware.CSRF(middleware.CSRFConfig{ TokenLength: 32, TokenLookup: "header:X-CSRF-Token", CookieName: "csrf_token", CookieSecure: true, CookieHTTPOnly: true, CookieSameSite: "Strict", }))
// Your routes app.Post("/api/users", createUser)
app.Listen(":8080")}Excluded Paths
Section titled “Excluded Paths”Some paths are automatically excluded from CSRF protection:
- Safe methods: GET, HEAD, OPTIONS
- WebSocket endpoint:
/realtime - Health checks:
/health,/ready - Metrics:
/metrics - API requests with API key authentication
Testing CSRF Protection
Section titled “Testing CSRF Protection”Manual Testing
Section titled “Manual Testing”1. Test without CSRF token:
# This should fail with 403 Forbiddencurl -X POST http://localhost:8080/api/v1/tables/users \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{"name":"John Doe"}'2. Test with valid CSRF token:
# First, get the CSRF token (from browser cookies or initial request)CSRF_TOKEN="your-csrf-token-here"
# This should succeedcurl -X POST http://localhost:8080/api/v1/tables/users \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "X-CSRF-Token: $CSRF_TOKEN" \ -b "csrf_token=$CSRF_TOKEN" \ -d '{"name":"John Doe"}'Automated Testing
Section titled “Automated Testing”import { describe, it, expect } from "vitest";import { createClient } from "@nimbleflux/fluxbase-sdk";
describe("CSRF Protection", () => { it("should reject requests without CSRF token", async () => { const client = createClient("http://localhost:8080", "your-anon-key");
await client.auth.signIn({ email: "user@example.com", password: "password", });
try { // Manually make request without CSRF token await fetch("http://localhost:8080/api/v1/tables/users", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${client.getAuthToken()}`, }, body: JSON.stringify({ name: "John" }), });
// Should not reach here expect(true).toBe(false); } catch (error) { expect(error.response.status).toBe(403); } });
it("should accept requests with valid CSRF token", async () => { const client = createClient("http://localhost:8080", "your-anon-key");
await client.auth.signIn({ email: "user@example.com", password: "password", });
// SDK automatically handles CSRF const { data, error } = await client .from("users") .insert({ name: "John Doe", }) .execute();
expect(error).toBeNull(); expect(data).toBeDefined(); });});Common Issues
Section titled “Common Issues”Issue: “CSRF token validation failed”
Section titled “Issue: “CSRF token validation failed””Cause: CSRF token missing or invalid
Solutions:
-
Ensure cookie is being sent:
fetch(url, {credentials: "include", // Include cookies}); -
Check cookie domain matches your application domain.
-
Verify token is included in header:
headers: {'X-CSRF-Token': csrfToken}
Issue: “CSRF token expired”
Section titled “Issue: “CSRF token expired””Cause: Token older than configured expiration
Solution: The SDK handles token refresh automatically. If you encounter this issue, any SDK call will refresh the token:
// Any SDK request will refresh the CSRF tokenawait client.admin.getHealth();Issue: CSRF with CORS
Section titled “Issue: CSRF with CORS”Cause: Cross-origin requests with credentials
Solution: Configure CORS properly:
cors: allowed_origins: "https://yourdomain.com" allow_credentials: true// Client must include credentialsfetch(url, { credentials: "include", // Send cookies cross-origin headers: { "X-CSRF-Token": csrfToken, },});Issue: CSRF in mobile apps
Section titled “Issue: CSRF in mobile apps”Cause: Mobile apps don’t use cookies like browsers
Solution: Use API key authentication instead:
// Mobile app using API key (no CSRF needed)const client = createClient("https://api.yourdomain.com", "your-anon-key");Best Practices
Section titled “Best Practices”1. Always Use HTTPS in Production
Section titled “1. Always Use HTTPS in Production”TLS is not built into Fluxbase. Use a reverse proxy (nginx, Caddy, Traefik) for HTTPS.
2. Use Strict SameSite Cookies
Section titled “2. Use Strict SameSite Cookies”SameSite cookies are set to Strict by default, providing the highest level of CSRF protection.
3. Set Reasonable Expiration
Section titled “3. Set Reasonable Expiration”CSRF tokens have a built-in expiration. The SDK handles token refresh automatically.
4. Rotate Tokens After Sensitive Actions
Section titled “4. Rotate Tokens After Sensitive Actions”// After password change, the SDK automatically handles token refreshawait client.auth.changePassword(oldPassword, newPassword);
// The next request will use a fresh CSRF token5. Monitor CSRF Failures
Section titled “5. Monitor CSRF Failures”// Log CSRF failures for security monitoringapp.Use(func(c *fiber.Ctx) error { err := c.Next() if err != nil && err.Error() == "CSRF token validation failed" { log.Warn(). Str("ip", c.IP()). Str("path", c.Path()). Msg("CSRF validation failed") } return err})Security Considerations
Section titled “Security Considerations”CSRF vs XSS
Section titled “CSRF vs XSS”CSRF protection doesn’t prevent XSS attacks. Always:
- Implement Content Security Policy
- Sanitize user input
- Use secure templating
- Enable security headers
CSRF vs client keys
Section titled “CSRF vs client keys”API key authentication bypasses CSRF protection:
- Client keys are not stored in cookies
- Intended for server-to-server communication
- Still need proper authentication and authorization
Token Storage
Section titled “Token Storage”Never store CSRF tokens in:
- ❌ LocalStorage (vulnerable to XSS)
- ❌ SessionStorage (vulnerable to XSS)
- ✅ HTTP-only cookies (safe from JavaScript)
Further Reading
Section titled “Further Reading”Summary
Section titled “Summary”Fluxbase provides robust CSRF protection out of the box:
- ✅ Double-submit cookie pattern
- ✅ Automatic token generation
- ✅ HTTP-only cookies
- ✅ SameSite attribute support
- ✅ Configurable expiration
- ✅ SDK handles tokens automatically
Enable CSRF protection in production and follow best practices to protect your users from CSRF attacks.