security: Fix critical vulnerabilities from security audit
- Remove sensitive tokens from logs (show only 8-char preview) - Enforce SECRET_KEY minimum 32 characters (no default value) - Reduce login rate limit from 100/hour to 5/hour - Remove exposed PageSpeed API key from CLAUDE.md BREAKING: Application requires SECRET_KEY >= 32 chars in .env Author: Maciej Pienczyn z wykorzystaniem AI i Claude Opus
This commit is contained in:
parent
39a91b709a
commit
5af216c5e0
@ -617,12 +617,10 @@ Wykorzystuje Google PageSpeed Insights API do analizy wydajności i jakości str
|
||||
- Limit: 25,000 zapytań/dzień (free tier)
|
||||
- Endpoint: `https://www.googleapis.com/pagespeedonline/v5/runPagespeed`
|
||||
|
||||
**Aktualny klucz (2026-01-08):**
|
||||
**Klucz API:**
|
||||
- **Nazwa w Google Cloud:** `Page SPEED SEO Audit v2`
|
||||
- **Wartość:**
|
||||
```
|
||||
GOOGLE_PAGESPEED_API_KEY=AIzaSyC9OAvPVCHsmPuMOv5gETyXXAdAe8J60Yw
|
||||
```
|
||||
- **Wartość:** Przechowywany w `.env` (GOOGLE_PAGESPEED_API_KEY)
|
||||
- **UWAGA:** Nigdy nie commituj kluczy API do repozytorium!
|
||||
|
||||
### Metryki audytu
|
||||
|
||||
|
||||
30
app.py
30
app.py
@ -151,7 +151,13 @@ except ImportError as e:
|
||||
|
||||
# Initialize Flask app
|
||||
app = Flask(__name__)
|
||||
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production')
|
||||
|
||||
# Security: Require strong SECRET_KEY (no default value allowed)
|
||||
SECRET_KEY = os.getenv('SECRET_KEY')
|
||||
if not SECRET_KEY or len(SECRET_KEY) < 32:
|
||||
raise ValueError("SECRET_KEY must be set in environment variables and be at least 32 characters long")
|
||||
app.config['SECRET_KEY'] = SECRET_KEY
|
||||
|
||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)
|
||||
|
||||
# Security configurations
|
||||
@ -2600,13 +2606,13 @@ def register():
|
||||
logger.info(f"Verification email sent to {email}")
|
||||
else:
|
||||
logger.warning(f"Failed to send verification email to {email}")
|
||||
logger.info(f"Verification URL (email failed): {verification_url}")
|
||||
logger.info(f"Verification token (email failed) for {email}: {verification_token[:8]}...")
|
||||
else:
|
||||
logger.warning("Email service not configured")
|
||||
logger.info(f"Verification URL (no email service): {verification_url}")
|
||||
logger.info(f"Verification token (no email) for {email}: {verification_token[:8]}...")
|
||||
except Exception as e:
|
||||
logger.error(f"Error sending verification email: {e}")
|
||||
logger.info(f"Verification URL (exception): {verification_url}")
|
||||
logger.info(f"Verification token (exception) for {email}: {verification_token[:8]}...")
|
||||
|
||||
logger.info(f"New user registered: {email}")
|
||||
flash('Rejestracja udana! Sprawdz email i kliknij link weryfikacyjny.', 'success')
|
||||
@ -2623,7 +2629,7 @@ def register():
|
||||
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
@limiter.limit("100 per hour") # Increased for testing
|
||||
@limiter.limit("5 per hour") # Strict limit to prevent brute force attacks
|
||||
def login():
|
||||
"""User login"""
|
||||
if current_user.is_authenticated:
|
||||
@ -2730,14 +2736,14 @@ def forgot_password():
|
||||
logger.info(f"Password reset email sent to {email}")
|
||||
else:
|
||||
logger.warning(f"Failed to send password reset email to {email}")
|
||||
# Log URL for manual recovery
|
||||
logger.info(f"Reset URL (email failed): {reset_url}")
|
||||
# Log token preview for debugging (full token never logged for security)
|
||||
logger.info(f"Reset token (email failed) for {email}: {reset_token[:8]}...")
|
||||
else:
|
||||
logger.warning("Email service not configured")
|
||||
logger.info(f"Reset URL (no email service): {reset_url}")
|
||||
logger.info(f"Reset token (no email) for {email}: {reset_token[:8]}...")
|
||||
except Exception as e:
|
||||
logger.error(f"Error sending reset email: {e}")
|
||||
logger.info(f"Reset URL (exception): {reset_url}")
|
||||
logger.info(f"Reset token (exception) for {email}: {reset_token[:8]}...")
|
||||
|
||||
# Always show same message to prevent email enumeration
|
||||
flash('Jeśli email istnieje w systemie, instrukcje resetowania hasła zostały wysłane.', 'info')
|
||||
@ -2886,13 +2892,13 @@ def resend_verification():
|
||||
logger.info(f"Verification email resent to {email}")
|
||||
else:
|
||||
logger.warning(f"Failed to resend verification email to {email}")
|
||||
logger.info(f"Verification URL (email failed): {verification_url}")
|
||||
logger.info(f"Resend verification token (email failed) for {email}: {verification_token[:8]}...")
|
||||
else:
|
||||
logger.warning("Email service not configured")
|
||||
logger.info(f"Verification URL (no email service): {verification_url}")
|
||||
logger.info(f"Resend verification token (no email) for {email}: {verification_token[:8]}...")
|
||||
except Exception as e:
|
||||
logger.error(f"Error resending verification email: {e}")
|
||||
logger.info(f"Verification URL (exception): {verification_url}")
|
||||
logger.info(f"Resend verification token (exception) for {email}: {verification_token[:8]}...")
|
||||
|
||||
# Always show same message to prevent email enumeration
|
||||
flash('Jesli konto istnieje i nie zostalo zweryfikowane, email weryfikacyjny zostal wyslany.', 'info')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user