Security
Cloudwerk provides comprehensive security middleware to protect your application from common web vulnerabilities. This guide covers security best practices and how to implement them.
Quick Start
Section titled “Quick Start”The fastest way to secure your app is with the combined security middleware:
// app/middleware.tsimport { securityMiddleware } from '@cloudwerk/security/middleware'
export const middleware = securityMiddleware({ allowedOrigins: ['https://myapp.com'],})This enables:
- CSRF protection - Validates tokens on mutation requests
- X-Requested-With validation - Forces CORS preflight
- Security headers - Sets protective HTTP headers
Understanding the Protections
Section titled “Understanding the Protections”CSRF Protection
Section titled “CSRF Protection”Cross-Site Request Forgery (CSRF) attacks trick authenticated users into making unwanted requests. Cloudwerk uses the double-submit cookie pattern:
- Server sets a CSRF token in a cookie
- Client sends the same token in a header
- Server validates they match
// Server validates on mutation requests (POST, PUT, PATCH, DELETE)// Cookie: cloudwerk.csrf-token=abc123// Header: X-CSRF-Token: abc123X-Requested-With Header
Section titled “X-Requested-With Header”The X-Requested-With: XMLHttpRequest header provides defense-in-depth:
- Custom headers trigger CORS preflight requests
- Cross-origin attackers can’t set custom headers without preflight approval
- Acts as an additional layer even if CSRF is somehow bypassed
Security Headers
Section titled “Security Headers”Cloudwerk sets these headers by default:
| Header | Default | Purpose |
|---|---|---|
X-Content-Type-Options | nosniff | Prevents MIME type sniffing |
X-Frame-Options | DENY | Prevents clickjacking |
Referrer-Policy | strict-origin-when-cross-origin | Controls referrer information |
X-XSS-Protection | 0 | Disabled (obsolete, can cause issues) |
Making Secure API Requests
Section titled “Making Secure API Requests”Use the client helpers to automatically include security headers:
import { secureFetch } from '@cloudwerk/security/client'
// Automatically adds X-CSRF-Token and X-Requested-Withconst response = await secureFetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Alice' }),})import { securePost, secureDelete } from '@cloudwerk/security/client'
// POST with auto JSON serializationconst user = await securePost('/api/users', { name: 'Alice' })
// DELETEawait secureDelete(`/api/users/${userId}`)import { getCsrfToken } from '@cloudwerk/security/client'
// If you need manual controlconst response = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': getCsrfToken() ?? '', 'X-Requested-With': 'XMLHttpRequest', }, body: JSON.stringify({ name: 'Alice' }),})Working with Forms
Section titled “Working with Forms”For traditional form submissions, include the CSRF token as a hidden field:
import { getCsrfToken } from '@cloudwerk/security/client'
function ContactForm() { return ( <form method="POST" action="/api/contact"> <input type="hidden" name="csrf_token" value={getCsrfToken() ?? ''} /> <input type="email" name="email" /> <button type="submit">Submit</button> </form> )}The csrfInput() helper returns an HTML string for use in templates:
import { csrfInput } from '@cloudwerk/security/client'
// Returns: '<input type="hidden" name="csrf_token" value="..." />'const inputHtml = csrfInput()Excluding Paths
Section titled “Excluding Paths”Some endpoints like webhooks need to bypass security checks:
export const middleware = securityMiddleware({ csrf: { excludePaths: ['/api/webhooks/stripe', '/api/webhooks/github'], }, requestedWith: { excludePaths: ['/api/webhooks'], },})Adding Content Security Policy
Section titled “Adding Content Security Policy”CSP is disabled by default because it requires app-specific configuration:
export const middleware = securityMiddleware({ csp: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'", 'https://cdn.example.com'], styleSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", 'data:', 'https:'], connectSrc: ["'self'", 'https://api.example.com'], }, reportOnly: true, // Test before enforcing },})Adding Origin Validation
Section titled “Adding Origin Validation”For additional cross-origin protection, validate the Origin header:
export const middleware = securityMiddleware({ allowedOrigins: ['https://myapp.com'], origin: { allowSubdomains: true, // Also allows api.myapp.com, admin.myapp.com rejectMissingOrigin: false, // Allow requests without Origin (needed for some webhooks) },})CSRF Token Rotation
Section titled “CSRF Token Rotation”After authentication state changes, rotate the CSRF token to prevent session fixation:
import { rotateCsrfToken } from '@cloudwerk/security'
export async function handleLogin(request: Request) { const user = await validateCredentials(request) const session = await createSession(user)
let response = createAuthResponse(user, session) response = rotateCsrfToken(response) // New token bound to session return response}Disabling Protections
Section titled “Disabling Protections”You can disable specific protections if needed:
export const middleware = securityMiddleware({ csrf: false, // Disable CSRF (not recommended) requestedWith: false, // Disable X-Requested-With validation headers: false, // Disable security headers})Individual Middleware
Section titled “Individual Middleware”Use individual middleware functions for fine-grained control:
import { csrfMiddleware, securityHeadersMiddleware, cspMiddleware, originValidationMiddleware, requestedWithMiddleware,} from '@cloudwerk/security/middleware'
// Apply only what you needexport const middleware = csrfMiddleware({ excludePaths: ['/api/webhooks'],})Security Checklist
Section titled “Security Checklist”-
Enable security middleware on all routes that handle user data
-
Use secureFetch for all API requests from the client
-
Rotate CSRF tokens after login, logout, and password changes
-
Configure CSP once your app is stable
-
Verify webhooks using provider signatures, not just CSRF bypass
-
Set allowed origins for production deployments
-
Test with report-only before enforcing CSP
Common Issues
Section titled “Common Issues”403 Forbidden on POST requests
Section titled “403 Forbidden on POST requests”The request is missing required security headers. Use secureFetch() or manually add:
X-CSRF-Tokenheader with the token from the cookieX-Requested-With: XMLHttpRequestheader
CSRF token mismatch after login
Section titled “CSRF token mismatch after login”The CSRF token rotates after authentication. Fetch a new token or reload the page to get the updated cookie.
CSP blocking inline scripts
Section titled “CSP blocking inline scripts”Either add the script source to scriptSrc, use nonces, or move the script to an external file.
Next Steps
Section titled “Next Steps”- Security API Reference - Complete API documentation
- Authentication Guide - Protect routes with auth
- Auth API - Authentication system reference