244 lines
9.1 KiB
Markdown
244 lines
9.1 KiB
Markdown
<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>
|