| title | CSRF Protection |
|---|---|
| sidebar_position | 6 |
The gateway provides stateless CSRF (Cross-Site Request Forgery) protection using the double-submit cookie pattern with HMAC-signed tokens, plus optional Origin/Referer validation for defense in depth.
- Safe methods (GET, HEAD, OPTIONS, TRACE by default): The gateway sets a
_csrfcookie containing an HMAC-signed, timestamped token. - State-changing methods (POST, PUT, DELETE, PATCH): The client must send the token value in both the cookie and a request header (
X-CSRF-Token). The gateway verifies that:- Both cookie and header are present and match
- The token has a valid HMAC signature (proving it was issued by the gateway)
- The token has not expired (based on
token_ttl)
- Origin validation (optional): If
allowed_originsorallowed_origin_patternsare configured, theOriginheader (orRefererfallback) must match.
This is a stateless mechanism — no server-side session storage is needed. The HMAC secret is the shared key that proves token authenticity.
csrf:
enabled: true
secret: "${CSRF_SECRET}" # Required. HMAC signing key.
cookie_name: "_csrf" # Default: "_csrf"
header_name: "X-CSRF-Token" # Default: "X-CSRF-Token"
token_ttl: 1h # Default: 1h
cookie_secure: true # Default: set explicitly in YAML
cookie_samesite: "lax" # "strict", "lax", or "none". Default: "lax"
cookie_http_only: false # Must be false so JS can read the cookie
inject_token: true # Set token cookie on safe methods. Default: set explicitly
allowed_origins: # Optional: exact origin allow list
- "https://app.example.com"
allowed_origin_patterns: # Optional: regex patterns
- "^https://.*\\.example\\.com$"
shadow_mode: false # Log failures without rejecting
exempt_paths: # Glob patterns that skip CSRF checks
- "/api/webhooks"
- "/health"routes:
- id: "web-app"
path: "/app"
path_prefix: true
backends:
- url: "http://backend:8080"
csrf:
enabled: true
secret: "${CSRF_SECRET}"
cookie_secure: true
inject_token: true
allowed_origins:
- "https://app.example.com"Per-route config is merged with the global csrf: block. Per-route non-zero fields override global values.
- Client sends
GET /pageto the runway - Runway sets
Set-Cookie: _csrf=<token>; Path=/; SameSite=Lax; Secure - Client-side JavaScript reads the cookie and includes it in subsequent requests:
const token = document.cookie.match(/(?:^|;\s*)_csrf=([^;]*)/)?.[1]; fetch('/api/submit', { method: 'POST', headers: { 'X-CSRF-Token': token, 'Content-Type': 'application/json' }, credentials: 'same-origin', body: JSON.stringify(data) });
- Runway validates: cookie == header, valid HMAC, not expired
When allowed_origins or allowed_origin_patterns are configured, the gateway checks the Origin header on state-changing requests. If Origin is absent, it falls back to the Referer header's scheme+host. If neither is present, the request is rejected.
This provides defense in depth — even if an attacker manages to obtain a valid token, the origin check prevents cross-site submission.
Set shadow_mode: true to log CSRF failures without rejecting requests. This is useful for gradual rollout:
- Deploy with
shadow_mode: true - Monitor logs for
CSRF token missing (shadow mode)and similar warnings - Once clients are updated to send tokens, disable shadow mode
By default, GET, HEAD, OPTIONS, and TRACE are considered safe (no token validation). Override with:
csrf:
safe_methods: ["GET", "HEAD", "OPTIONS"]Paths matching exempt_paths glob patterns skip CSRF validation entirely. Use for webhook endpoints or health checks:
csrf:
exempt_paths:
- "/api/webhooks*"
- "/health"| Field | Default | Description |
|---|---|---|
cookie_name |
_csrf |
Cookie name |
cookie_path |
/ |
Cookie path |
cookie_domain |
(empty) | Cookie domain |
cookie_secure |
Set in YAML | Secure flag (required for SameSite=None) |
cookie_samesite |
lax |
SameSite attribute: strict, lax, none |
cookie_http_only |
false |
HttpOnly flag (must be false for JS access) |
secretis required whenenabled: truecookie_samesite: "none"requirescookie_secure: truetoken_ttlmust be >= 0allowed_origin_patternsmust be valid regular expressions
Returns per-route CSRF protection status and metrics.
curl http://localhost:8081/csrfResponse:
{
"web-app": {
"cookie_name": "_csrf",
"header_name": "X-CSRF-Token",
"token_ttl": "1h0m0s",
"shadow_mode": false,
"inject_token": true,
"total_requests": 5000,
"token_generated": 2000,
"validation_success": 2900,
"validation_failed": 100,
"origin_check_failed": 5,
"missing_token": 80,
"expired_token": 10,
"invalid_signature": 5
}
}- Use a strong, random
secret(at least 32 bytes). Use environment variable substitution:secret: "${CSRF_SECRET}". - Set
cookie_secure: truein production to prevent cookie transmission over HTTP. - Set
cookie_samesite: "lax"(default) or"strict"to prevent most cross-site cookie sending. - Keep
cookie_http_only: falseso client-side JavaScript can read the cookie value and send it as a header. The HMAC signature prevents forgery even if the cookie is readable. - The token's HMAC proves it was issued by the gateway. An attacker cannot forge tokens without the secret.
- Origin validation provides an additional layer — even with a stolen token, the browser will send the real origin.