Security hardening
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user