04/28 Fixed some security issues

This commit is contained in:
2026-04-28 16:27:39 -04:00
parent d8b57cde77
commit bfc2f58fe2
38 changed files with 4273 additions and 4067 deletions
+32 -21
View File
@@ -60,24 +60,24 @@ class SecurityManager:
self.register_security_routes()
def setup_encryption(self):
"""Setup encryption for sensitive data"""
"""Setup encryption for sensitive data.
Derives a stable Fernet key from the app's SECRET_KEY so that all
gunicorn workers share the same key without needing a separate
ENCRYPTION_KEY env var. A random key is only generated as a last
resort (dev mode without SECRET_KEY set).
"""
if HAS_CRYPTOGRAPHY:
encryption_key = self.app.config.get('ENCRYPTION_KEY')
if not encryption_key:
# Generate a new key (should be stored securely in production)
encryption_key = Fernet.generate_key()
if self.logger_handler:
self.logger_handler.logger.warning(
"Generated new encryption key - store this securely!"
)
# Derive a deterministic 32-byte key from SECRET_KEY so every
# worker produces the same value — no per-worker randomness.
secret = self.app.config.get('SECRET_KEY', '')
derived = hashlib.sha256(secret.encode()).digest()
encryption_key = base64.urlsafe_b64encode(derived)
self.cipher = Fernet(encryption_key)
else:
self.cipher = None
if self.logger_handler:
self.logger_handler.logger.warning(
"Cryptography not available - encryption features disabled"
)
def security_check(self):
"""Comprehensive security check before each request"""
@@ -93,12 +93,12 @@ class SecurityManager:
})
return jsonify({'error': 'Request blocked for security reasons'}), 403
# Validate session security
if 'user_id' in session:
if not self.validate_session_security():
session.clear()
return jsonify({'error': 'Session security validation failed'}), 401
# NOTE: per-worker in-memory session token validation removed.
# Flask's cryptographically signed session cookie provides session
# integrity; CSRF tokens handle cross-site forgery. Keeping the
# validate_session_security() call here would log users out on every
# gunicorn worker boundary because session_tokens is not shared.
# Check for SQL injection attempts
if self.detect_sql_injection():
self.log_security_event('sql_injection_attempt', {
@@ -212,9 +212,20 @@ class SecurityManager:
check_data = []
check_data.extend(request.args.values())
check_data.extend(request.form.values())
if request.json:
check_data.extend(str(v) for v in request.json.values() if isinstance(v, (str, int, float)))
# Only attempt JSON parsing when the client declared application/json.
# Calling request.json without this guard raises a 415 Unsupported Media Type
# on every non-JSON request (GET pages, form POSTs, favicon, etc.).
if request.content_type and 'application/json' in request.content_type:
try:
json_body = request.get_json(silent=True, force=False)
if json_body and isinstance(json_body, dict):
check_data.extend(
str(v) for v in json_body.values()
if isinstance(v, (str, int, float))
)
except Exception:
pass
for data in check_data:
data_str = str(data).lower()