CSRF Token Security - Class Notes
What is CSRF?
Cross-Site Request Forgery (CSRF) is a web security vulnerability that allows an attacker to induce users
to perform actions they did not intend to perform on a web application where they are authenticated.
Key Characteristics:
Exploits the trust a web application has in a user's browser
Takes advantage of automatic inclusion of cookies in requests
Victim must be authenticated to the target application
Attack occurs without the victim's knowledge
How CSRF Attacks Work
Attack Flow:
1. Victim logs in to legitimate website (e.g., bank.com)
2. Session established - browser stores authentication cookies
3. Victim visits malicious site while still logged in to legitimate site
4. Malicious site triggers request to legitimate site using victim's credentials
5. Legitimate site processes request believing it came from authenticated user
Example Scenario:
User logs into bank.com → Session cookie stored
User visits evil.com → Contains hidden form:
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="to" value="attacker_account">
<input type="hidden" name="amount" value="10000">
</form>
<script>document.forms[0].submit();</script>
CSRF Tokens - The Primary Defense
What are CSRF Tokens?
Unique, unpredictable values generated by the server
Tied to user's session but not stored in cookies
Required for state-changing operations
Validated on server side before processing requests
Token Properties:
Cryptographically random - impossible to guess
Session-specific - unique per user session
Request-specific (optional) - can be unique per form/request
Time-limited (optional) - can expire after set period
Implementation Strategies
1. Synchronizer Token Pattern
html
<!-- Token embedded in form -->
<form action="/transfer" method="POST">
<input type="hidden" name="csrf_token" value="abc123xyz789">
<input type="text" name="amount">
<button type="submit">Transfer</button>
</form>
Server-side validation:
python
def process_transfer(request):
session_token = request.session.get('csrf_token')
form_token = request.POST.get('csrf_token')
if not session_token or session_token != form_token:
return HttpResponseForbidden("CSRF token mismatch")
# Process legitimate request
perform_transfer(request.POST['amount'])
2. Double Submit Cookie Pattern
Token sent both as cookie AND in request body/header
Server compares both values
Simpler implementation, no server-side storage needed
javascript
// Client-side implementation
fetch('/api/transfer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCookie('csrf_token')
},
body: JSON.stringify({
amount: 1000,
csrf_token: getCookie('csrf_token')
})
});
3. Token Generation Best Practices
Strong Token Generation:
python
import secrets
import hashlib
def generate_csrf_token(session_id):
# Method 1: Pure random
return secrets.token_urlsafe(32)
# Method 2: HMAC-based
secret_key = get_secret_key()
message = f"{session_id}:{timestamp}"
return hmac.new(secret_key, message.encode(), hashlib.sha256).hexdigest()
Advanced CSRF Protection Techniques
1. SameSite Cookie Attribute
http
Set-Cookie: sessionid=abc123; SameSite=Strict; Secure; HttpOnly
SameSite Values:
Strict: Cookie never sent on cross-site requests
Lax: Cookie sent on top-level navigation (GET)
None: Cookie always sent (requires Secure flag)
2. Origin/Referer Header Validation
python
def validate_origin(request):
origin = request.META.get('HTTP_ORIGIN')
referer = request.META.get('HTTP_REFERER')
allowed_origins = ['https://mysite.com']
if origin and origin in allowed_origins:
return True
if referer and any(referer.startswith(o) for o in allowed_origins):
return True
return False
3. Custom Request Headers
javascript
// Attackers can't set custom headers in simple requests
fetch('/api/endpoint', {
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': getCSRFToken()
}
});
Framework-Specific Implementations
Django
python
# settings.py
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
]
# Template
{% csrf_token %}
# View
from django.views.decorators.csrf import csrf_protect
@csrf_protect
def my_view(request):
# Automatically protected
Express.js (Node.js)
javascript
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
app.use(csrfProtection);
app.get('/form', (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
Spring Security (Java)
java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
return http.build();
}
}
Common Implementation Pitfalls
1. Token Leakage
Don't include tokens in URLs (GET parameters)
Avoid logging tokens
Don't expose tokens in error messages
2. Weak Token Generation
python
# BAD - Predictable
token = str(time.time()) + user_id
# GOOD - Cryptographically random
token = secrets.token_urlsafe(32)
3. Insufficient Validation
python
# BAD - Only checks if token exists
if request.POST.get('csrf_token'):
process_request()
# GOOD - Validates token value
if validate_csrf_token(request.POST.get('csrf_token'), request.session):
process_request()
4. Missing HTTPS
CSRF tokens sent over HTTP can be intercepted
Always use HTTPS in production
Set Secure flag on cookies
Testing CSRF Protection
Manual Testing:
1. Remove token from form and submit
2. Modify token value and submit
3. Reuse old token after session change
4. Cross-origin requests without proper headers
Automated Testing:
python
def test_csrf_protection(self):
# Test missing token
response = self.client.post('/transfer', {'amount': 100})
self.assertEqual(response.status_code, 403)
# Test invalid token
response = self.client.post('/transfer', {
'amount': 100,
'csrf_token': 'invalid'
})
self.assertEqual(response.status_code, 403)
Security Considerations
When CSRF Protection May Not Be Enough:
XSS vulnerabilities can steal CSRF tokens
Subdomain attacks if cookies not properly scoped
JSONP endpoints may bypass same-origin policy
Flash/plugin vulnerabilities can make cross-origin requests
Defense in Depth:
1. CSRF tokens (primary defense)
2. SameSite cookies (additional protection)
3. Origin validation (backup check)
4. Content Security Policy (XSS mitigation)
5. Regular security audits
Key Takeaways
1. CSRF tokens are essential for protecting state-changing operations
2. Tokens must be unpredictable and tied to user sessions
3. Validate tokens server-side for every protected request
4. Use framework built-ins when available
5. Combine with other protections like SameSite cookies
6. Test thoroughly to ensure implementation works correctly
7. Never rely on client-side validation alone
Additional Resources
OWASP CSRF Prevention Cheat Sheet
Framework-specific documentation
Security testing tools (Burp Suite, OWASP ZAP)
Regular security assessments and code reviews