Files

244 lines
9.1 KiB
Markdown
Raw Permalink Normal View History

2025-12-27 17:50:30 +01:00
<objective>
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.
</objective>
<context>
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
</context>
<requirements>
<csrf_protection>
**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
</csrf_protection>
<rate_limiting>
**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/<session_id>/submit-personal
- /apply/<session_id>/submit-motivation
- /apply/<session_id>/upload-file
- /apply/<session_id>/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)
</rate_limiting>
<security_best_practices>
- 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)
</security_best_practices>
</requirements>
<implementation>
**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 `<form>` tag:
```html
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
```
4. **Implement Rate Limiting Decorator/Function**:
Create a helper function to check rate limits:
```python
def check_rate_limit():
"""Check if user is submitting forms too quickly."""
from datetime import datetime
from flask import session
now = datetime.now()
last_submit = session.get('last_submission_time')
if last_submit:
# Parse stored timestamp and check elapsed time
# If < 5 seconds, return False and remaining wait time
# Else return True
session['last_submission_time'] = now.isoformat()
return True
```
5. **Update All POST Routes**:
- Add rate limit check at the beginning of each POST handler
- Return 429 error with flash message if rate limited
- CSRF validation happens automatically via Flask-WTF
6. **Add Error Handlers**:
```python
@app.errorhandler(CSRFError)
def handle_csrf_error(e):
flash('Sicherheitsfehler: Bitte laden Sie die Seite neu und versuchen Sie es erneut.', 'error')
return redirect(url_for('page1_email'))
```
7. **Update Configuration**:
- Add rate limiting settings to config.py
- Ensure session configuration is secure
**What to Avoid and Why**:
- Don't disable CSRF for any POST routes - all forms need protection to prevent CSRF attacks
- Don't use IP-based rate limiting alone - users behind NAT/proxies share IPs; session-based is more accurate per-user
- Don't make rate limit too aggressive (< 5 seconds) - disrupts legitimate users trying to correct mistakes
- Don't forget to update tests - CSRF protection will break existing tests that don't include tokens
- Don't store sensitive data in rate limit error messages - only show wait time, not internal state
</implementation>
<output>
Modify the following files:
1. `./requirements.txt` - Add Flask-WTF dependency
2. `./app.py` - Add CSRF protection, rate limiting logic, error handlers
3. `./config.py` - Add rate limiting and CSRF configuration
4. `./templates/page1_email.html` - Add CSRF token
5. `./templates/page2_personal.html` - Add CSRF token
6. `./templates/page3_motivation.html` - Add CSRF token
7. `./templates/page4_upload.html` - Add CSRF tokens (2 forms)
8. `./tests/test_routes.py` - Update tests to include CSRF tokens
9. `./tests/conftest.py` - Update test configuration to handle CSRF in tests
All modifications should maintain existing functionality while adding security layers.
</output>
<verification>
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
</verification>
<success_criteria>
- 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
</success_criteria>