04/28 Fixed some security issues
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user