CSRF Token is one of the most effective methods for defending against CSRF attacks, preventing cross-site request forgery by verifying the legitimacy of requests.
How It Works
1. Token Generation
- Server generates random Token in user session
- Token must be sufficiently random (recommended at least 128 bits)
- Token can have expiration time
- Token is bound to user session
2. Token Transmission
- Token transmitted through form hidden field
- Or through custom request header (e.g.,
X-CSRF-Token) - Token can also be stored in Cookie (double submit)
3. Token Verification
- Server verifies Token upon receiving request
- Checks if Token in request matches session Token
- Verifies if Token has expired
- Verifies if Token has been used (optional)
Implementation Flow
shellUser visits page → Server generates CSRF Token → Token stored in session ↓ Token added to form/request header → User submits form/sends request ↓ Server verifies Token → Token matches → Execute operation ↓ Token doesn't match → Reject request
Code Implementation
Backend Implementation (Node.js + Express)
javascriptconst express = require('express'); const crypto = require('crypto'); const session = require('express-session'); const app = express(); app.use(session({ secret: 'your-secret-key', resave: false, saveUninitialized: true })); // Generate CSRF Token function generateCSRFToken() { return crypto.randomBytes(32).toString('hex'); } // Middleware: Generate Token app.use((req, res, next) => { if (!req.session.csrfToken) { req.session.csrfToken = generateCSRFToken(); } res.locals.csrfToken = req.session.csrfToken; next(); }); // Middleware: Verify Token function validateCSRFToken(req, res, next) { const token = req.body._csrf || req.headers['x-csrf-token']; if (!token || token !== req.session.csrfToken) { return res.status(403).json({ error: 'Invalid CSRF token' }); } next(); } // Protected route app.post('/transfer', validateCSRFToken, (req, res) => { // Handle transfer logic res.json({ success: true }); }); app.listen(3000);
Frontend Implementation
html<!-- Form submission --> <form action="/transfer" method="POST"> <input type="hidden" name="_csrf" value="{{ csrfToken }}"> <input type="number" name="amount" placeholder="Amount"> <button type="submit">Transfer</button> </form> <!-- AJAX request --> <script> const csrfToken = '{{ csrfToken }}'; fetch('/transfer', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, body: JSON.stringify({ amount: 100 }) }) .then(response => response.json()) .then(data => console.log(data)); </script>
Spring Security Implementation
java@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .and() .authorizeRequests() .antMatchers("/public/**").permitAll() .anyRequest().authenticated(); } }
Django Implementation
python# settings.py MIDDLEWARE = [ 'django.middleware.csrf.CsrfViewMiddleware', # ... other middleware ] # Use in template <form method="post"> {% csrf_token %} <input type="text" name="amount"> <button type="submit">Submit</button> </form> # Verify in view from django.views.decorators.csrf import csrf_protect @csrf_protect def transfer(request): if request.method == 'POST': # Handle transfer logic pass
Token Generation Algorithm
Using Cryptographically Secure Random Number Generators
javascript// Node.js const crypto = require('crypto'); const token = crypto.randomBytes(32).toString('hex'); // Python import secrets token = secrets.token_hex(32) // Java import java.security.SecureRandom; import java.math.BigInteger; SecureRandom random = new SecureRandom(); String token = new BigInteger(130, random).toString(32);
Security Considerations
1. Token Randomness
- Use cryptographically secure random number generators
- Token length at least 128 bits (32 bytes)
- Avoid using predictable Tokens
2. Token Expiration
- Token should have expiration time
- Recommended to set reasonable expiration time (e.g., 1-2 hours)
- Sensitive operations can use one-time Tokens
3. Token Storage
- Token stored in server session
- Don't store sensitive information on client
- Use HttpOnly Cookie to store Token (double submit)
4. Token Transmission
- Use HTTPS to transmit Token
- Avoid passing Token in URL
- Use POST method to submit forms
5. Token Verification
- Verify if Token matches
- Verify if Token has expired
- Verify request source (optional)
Common Issues
1. Token Expiration
- Session expiration causes Token to expire
- Solution: Automatically refresh Token or extend session time
2. Multiple Tabs Issue
- Multiple tabs share same Token
- Solution: Use independent Token per tab or share Token
3. AJAX Requests
- Need to add Token in request header
- Solution: Use interceptor to automatically add Token
4. File Upload
- File upload cannot use form Token
- Solution: Use request header or pre-signed URL
Best Practices
- Use framework's built-in CSRF protection: Like Spring Security, Django
- Bind Token to session: Use independent Token per session
- Set reasonable expiration time: Balance security and user experience
- Log Token usage: Facilitate auditing and monitoring
- Regularly update Token: Reduce Token leakage risk
- Combine with other protection measures: Like SameSite Cookie, Origin verification
Summary
CSRF Token effectively prevents CSRF attacks by verifying request legitimacy. Proper implementation of CSRF Token requires attention to Token generation, storage, transmission, and verification to ensure the security of the entire process. Using framework's built-in CSRF protection can greatly simplify the implementation.