CSRFConfig¶
Imagine you're a bank teller. Someone walks in with a withdrawal slip that looks legitimate, has the right account number, and seems properly filled out. But it's actually a forgery - the real account holder never authorized it.
CSRF (Cross-Site Request Forgery) attacks work the same way. An attacker tricks your browser into making requests that look legitimate because you're already logged in. CSRF protection is like adding a unique, time-sensitive code to each transaction that only the real user knows.
Protect your Ravyn application from CSRF attacks with built-in middleware.
What You'll Learn¶
- What CSRF is and why it's dangerous
- Configuring CSRF protection
- Using CSRF tokens in forms
- Best practices for CSRF security
Quick Start¶
from ravyn import Ravyn
from ravyn.config import CSRFConfig
app = Ravyn(
csrf_config=CSRFConfig(
secret="your-secret-key-here"
)
)
What is CSRF?¶
CSRF (Cross-Site Request Forgery) is an attack where a malicious site tricks users into performing unwanted actions on your site.
How CSRF Attacks Work¶
- User logs into your site (
example.com) - User visits malicious site (
evil.com) - Malicious site sends request to
example.comusing user's session - Your site processes the request (delete account, transfer money, etc.)
CSRF Protection¶
Ravyn's CSRF middleware generates unique tokens that must be included in state-changing requests (POST, PUT, DELETE).
Basic Configuration¶
Minimal Setup¶
from ravyn import Ravyn
from ravyn.config import CSRFConfig
app = Ravyn(
csrf_config=CSRFConfig(
secret="your-secret-key-change-this-in-production"
)
)
With Custom Settings¶
app = Ravyn(
csrf_config=CSRFConfig(
secret="your-secret-key",
cookie_name="csrftoken",
header_name="X-CSRF-Token",
cookie_secure=True, # HTTPS only
cookie_httponly=True
)
)
Configuration Parameters¶
| Parameter | Type | Description | Default |
|---|---|---|---|
secret |
str | Secret key for token generation | Required |
cookie_name |
str | CSRF cookie name | "csrftoken" |
header_name |
str | CSRF header name | "X-CSRF-Token" |
cookie_secure |
bool | HTTPS only cookie | False |
cookie_httponly |
bool | Prevent JavaScript access | True |
cookie_samesite |
str | SameSite attribute | "lax" |
Using CSRF Tokens¶
In HTML Forms¶
<form method="POST" action="/submit">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<input type="text" name="username">
<button type="submit">Submit</button>
</form>
In JavaScript/AJAX¶
// Get CSRF token from cookie
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
// Include in request header
fetch('/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCookie('csrftoken')
},
body: JSON.stringify({data: 'value'})
});
In Templates¶
<!-- Jinja2 template -->
<form method="POST">
{{ csrf_input }}
<!-- or -->
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="text" name="data">
<button>Submit</button>
</form>
Using with Settings¶
from ravyn import RavynSettings
from ravyn.config import CSRFConfig
class AppSettings(RavynSettings):
csrf_config: CSRFConfig = CSRFConfig(
secret="your-secret-key",
cookie_secure=True,
cookie_httponly=True
)
app = Ravyn(settings_module=AppSettings)
Common Pitfalls & Fixes¶
Pitfall 1: Missing Secret Key¶
Problem: No secret key configured.
# Wrong - no secret
csrf_config = CSRFConfig() # Error!
Solution: Always provide a secret:
# Correct
csrf_config = CSRFConfig(secret="your-secret-key")
Pitfall 2: AJAX Requests Failing¶
Problem: Forgot to include CSRF token in headers.
// Wrong - no CSRF token
fetch('/api/data', {
method: 'POST',
body: JSON.stringify({data: 'value'})
});
Solution: Include CSRF token:
// Correct
fetch('/api/data', {
method: 'POST',
headers: {
'X-CSRF-Token': getCookie('csrftoken')
},
body: JSON.stringify({data: 'value'})
});
Pitfall 3: Insecure Cookie in Production¶
Problem: Not using secure cookies over HTTPS.
# Wrong - insecure in production
csrf_config = CSRFConfig(
secret="key",
cookie_secure=False # Vulnerable!
)
Solution: Enable secure cookies:
# Correct
csrf_config = CSRFConfig(
secret="key",
cookie_secure=True, # HTTPS only
cookie_httponly=True
)
Best Practices¶
1. Use Strong Secret Keys¶
# Good - strong random secret
import secrets
csrf_config = CSRFConfig(
secret=secrets.token_urlsafe(32)
)
2. Enable Secure Cookies in Production¶
# Good - secure settings
import os
csrf_config = CSRFConfig(
secret=os.getenv("CSRF_SECRET"),
cookie_secure=True, # HTTPS only
cookie_httponly=True,
cookie_samesite="strict"
)
3. Use Environment Variables¶
# Good - configurable
import os
csrf_config = CSRFConfig(
secret=os.getenv("CSRF_SECRET", "dev-secret-change-in-prod")
)
Exempting Routes¶
Some routes (like webhooks) may need to bypass CSRF:
from ravyn import post
@post("/webhook", csrf_exempt=True)
async def webhook(data: dict) -> dict:
# Process webhook (no CSRF check)
return {"received": True}
Learn More¶
Next Steps¶
- CORSConfig - CORS configuration
- SessionConfig - Session management
- JWTConfig - JWT authentication