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)
|
- Limit: 25,000 zapytań/dzień (free tier)
|
||||||
- Endpoint: `https://www.googleapis.com/pagespeedonline/v5/runPagespeed`
|
- Endpoint: `https://www.googleapis.com/pagespeedonline/v5/runPagespeed`
|
||||||
|
|
||||||
**Aktualny klucz (2026-01-08):**
|
**Klucz API:**
|
||||||
- **Nazwa w Google Cloud:** `Page SPEED SEO Audit v2`
|
- **Nazwa w Google Cloud:** `Page SPEED SEO Audit v2`
|
||||||
- **Wartość:**
|
- **Wartość:** Przechowywany w `.env` (GOOGLE_PAGESPEED_API_KEY)
|
||||||
```
|
- **UWAGA:** Nigdy nie commituj kluczy API do repozytorium!
|
||||||
GOOGLE_PAGESPEED_API_KEY=AIzaSyC9OAvPVCHsmPuMOv5gETyXXAdAe8J60Yw
|
|
||||||
```
|
|
||||||
|
|
||||||
### Metryki audytu
|
### Metryki audytu
|
||||||
|
|
||||||
|
|||||||
30
app.py
30
app.py
@ -151,7 +151,13 @@ except ImportError as e:
|
|||||||
|
|
||||||
# Initialize Flask app
|
# Initialize Flask app
|
||||||
app = Flask(__name__)
|
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)
|
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)
|
||||||
|
|
||||||
# Security configurations
|
# Security configurations
|
||||||
@ -2600,13 +2606,13 @@ def register():
|
|||||||
logger.info(f"Verification email sent to {email}")
|
logger.info(f"Verification email sent to {email}")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Failed to send verification email to {email}")
|
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:
|
else:
|
||||||
logger.warning("Email service not configured")
|
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:
|
except Exception as e:
|
||||||
logger.error(f"Error sending verification email: {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}")
|
logger.info(f"New user registered: {email}")
|
||||||
flash('Rejestracja udana! Sprawdz email i kliknij link weryfikacyjny.', 'success')
|
flash('Rejestracja udana! Sprawdz email i kliknij link weryfikacyjny.', 'success')
|
||||||
@ -2623,7 +2629,7 @@ def register():
|
|||||||
|
|
||||||
|
|
||||||
@app.route('/login', methods=['GET', 'POST'])
|
@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():
|
def login():
|
||||||
"""User login"""
|
"""User login"""
|
||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated:
|
||||||
@ -2730,14 +2736,14 @@ def forgot_password():
|
|||||||
logger.info(f"Password reset email sent to {email}")
|
logger.info(f"Password reset email sent to {email}")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Failed to send password reset email to {email}")
|
logger.warning(f"Failed to send password reset email to {email}")
|
||||||
# Log URL for manual recovery
|
# Log token preview for debugging (full token never logged for security)
|
||||||
logger.info(f"Reset URL (email failed): {reset_url}")
|
logger.info(f"Reset token (email failed) for {email}: {reset_token[:8]}...")
|
||||||
else:
|
else:
|
||||||
logger.warning("Email service not configured")
|
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:
|
except Exception as e:
|
||||||
logger.error(f"Error sending reset email: {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
|
# Always show same message to prevent email enumeration
|
||||||
flash('Jeśli email istnieje w systemie, instrukcje resetowania hasła zostały wysłane.', 'info')
|
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}")
|
logger.info(f"Verification email resent to {email}")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Failed to resend verification email to {email}")
|
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:
|
else:
|
||||||
logger.warning("Email service not configured")
|
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:
|
except Exception as e:
|
||||||
logger.error(f"Error resending verification email: {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
|
# Always show same message to prevent email enumeration
|
||||||
flash('Jesli konto istnieje i nie zostalo zweryfikowane, email weryfikacyjny zostal wyslany.', 'info')
|
flash('Jesli konto istnieje i nie zostalo zweryfikowane, email weryfikacyjny zostal wyslany.', 'info')
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user