Implement comprehensive security hardening for the Flask job application system by adding CSRF (Cross-Site Request Forgery) protection to all forms and implementing rate limiting to prevent abuse of form submissions.
This security enhancement protects against CSRF attacks where malicious sites trick users into submitting forms, and prevents automated abuse or DoS attacks through aggressive form submission. The rate limiting ensures a minimum 5-second delay between form submissions to prevent spam and abuse.
The Flask job application system currently has 4 forms across the workflow:
- Page 1: Email submission form
- Page 2: Personal information form
- Page 3: Motivation questions form
- Page 4: File upload and final submission forms
Tech stack: Flask 3.0.0, Python 3, existing test suite with pytest
Current security gaps:
- No CSRF protection on forms (vulnerable to CSRF attacks)
- No rate limiting (vulnerable to automated submission abuse)
- Forms can be submitted repeatedly without delay
Examine these files to understand the current implementation:
@app.py - All routes and form handlers
@templates/*.html - All form templates
@config.py - Configuration settings
@tests/test_routes.py - Existing route tests that will need updating
**CSRF Token Implementation**
1. **Install and Configure Flask-WTF**:
- Add Flask-WTF to requirements.txt (provides CSRF protection)
- Configure CSRF protection in app.py
- Set secure CSRF configuration (token timeout, secure cookies)
2. **Add CSRF Tokens to All Forms**:
- Update all HTML templates to include CSRF tokens
- Add `{{ csrf_token() }}` hidden input to every form:
- templates/page1_email.html
- templates/page2_personal.html
- templates/page3_motivation.html
- templates/page4_upload.html (both upload form and submit form)
3. **Validate CSRF Tokens**:
- All POST routes must automatically validate CSRF tokens
- Return 400 Bad Request with clear error message on CSRF failure
- Ensure CSRF validation doesn't break the resume functionality
4. **CSRF Error Handling**:
- Add custom error handler for CSRF validation failures
- Display user-friendly German error messages
- Allow users to refresh and retry after CSRF errors
**Rate Limiting Implementation**
1. **Session-Based Rate Limiting**:
- Track last form submission timestamp in Flask session
- Enforce minimum 5-second delay between ANY form submissions
- Apply to all form POST routes:
- /apply/submit-email
- /apply//submit-personal
- /apply//submit-motivation
- /apply//upload-file
- /apply//submit-application
2. **Rate Limiting Logic**:
- Before processing any form submission, check session['last_submission_time']
- Calculate time elapsed since last submission
- If < 5 seconds, reject with HTTP 429 (Too Many Requests)
- If >= 5 seconds or first submission, allow and update timestamp
- Store timestamp in session for persistence across requests
3. **User-Friendly Rate Limit Messages**:
- Display remaining wait time in German
- Example: "Bitte warten Sie noch 3 Sekunden vor der nächsten Eingabe."
- Use flash messages to communicate rate limit errors
- Ensure message is clear and helps user understand the delay
4. **Rate Limit Exemptions**:
- GET requests are not rate limited (viewing pages)
- Resume links (loading existing applications) are not rate limited
- Only POST form submissions are rate limited
5. **Configuration**:
- Add RATE_LIMIT_SECONDS = 5 to config.py
- Make it configurable for different environments (dev, test, prod)
- Use secure session cookies (HttpOnly, Secure flags)
- Set proper SameSite attribute for cookies (Lax or Strict)
- Ensure CSRF tokens are cryptographically secure
- Rate limiting is per-session (prevents abuse from single source)
- Clear, informative error messages in German
- Maintain accessibility (forms remain usable with screen readers)
**Implementation Steps**
1. **Update Dependencies**:
- Add Flask-WTF==1.2.1 to requirements.txt
- Flask-WTF provides CSRFProtect extension
2. **Configure CSRF Protection in app.py**:
```python
from flask_wtf.csrf import CSRFProtect, CSRFError
csrf = CSRFProtect(app)
app.config['WTF_CSRF_TIME_LIMIT'] = None # Or set to reasonable limit
app.config['WTF_CSRF_SSL_STRICT'] = True
```
3. **Add CSRF Tokens to Templates**:
- Add after opening `
Before declaring complete, verify your implementation:
1. **Test CSRF Protection**:
```bash
# Try submitting a form without CSRF token - should fail
curl -X POST http://localhost:5000/apply/submit-email -d "email=test@example.com"
```
Expected: 400 Bad Request or CSRF error
2. **Test Rate Limiting**:
- Submit a form successfully
- Immediately try to submit another form
- Should see rate limit error with wait time
- Wait 5 seconds and submit again - should succeed
3. **Run Test Suite**:
```bash
pytest -v tests/test_routes.py
```
All tests should pass with CSRF tokens included
4. **Manual Testing**:
- Start the application
- Complete a full application workflow
- Verify CSRF tokens are present in all forms (view page source)
- Try rapid form submission - should see rate limit message
- Verify error messages are in German
5. **Security Verification**:
- Check that cookies have Secure and HttpOnly flags
- Verify CSRF tokens are different for each request
- Confirm rate limiting persists across different forms
6. **Accessibility Check**:
- Forms still work with keyboard navigation
- Screen readers can still use forms
- Error messages are announced properly
- Flask-WTF installed and configured correctly
- CSRF tokens present in all 5 forms (view page source)
- All POST routes validate CSRF tokens automatically
- CSRF error handler displays German error message
- Rate limiting active on all form submissions
- Minimum 5-second delay enforced between submissions
- Rate limit error shows remaining wait time in German
- Session-based rate limiting works correctly
- All existing tests updated and passing
- Manual workflow test completes successfully
- Security best practices implemented (secure cookies, etc.)
- Error handling is user-friendly and in German
- No functionality broken by security additions
- Application remains accessible and usable