Saas-Base / core /middleware /security.py
rsnarsna
sixth commit
8cab7c2
Raw
History Blame Contribute Delete
2.18 kB
"""
Security middleware — rate limiting and session fingerprint validation.
"""
from django.http import JsonResponse
from django.utils.deprecation import MiddlewareMixin
from apps.security.services.rate_limit_service import (
build_login_key,
check_rate_limit,
)
from core.exceptions import RateLimitError
from core.utils.ip import get_client_ip, get_device_fingerprint
class RateLimitMiddleware(MiddlewareMixin):
"""
Rate-limit login attempts by IP address.
Only applies to POST requests on the login URL.
"""
LOGIN_PATHS = ["/accounts/login/"]
def process_request(self, request):
if request.method != "POST":
return None
if request.path not in self.LOGIN_PATHS:
return None
ip = get_client_ip(request)
key = build_login_key(ip)
try:
check_rate_limit(key)
except RateLimitError as e:
return JsonResponse(
{"error": str(e)},
status=429,
)
return None
class SessionFingerprintMiddleware(MiddlewareMixin):
"""
Bind sessions to device fingerprint.
Validates that the current request's fingerprint matches
the session fingerprint. Logs a security event on mismatch.
"""
def process_request(self, request):
if not hasattr(request, "user") or not request.user.is_authenticated:
return None
current_fingerprint = get_device_fingerprint(request)
stored_fingerprint = request.session.get("device_fingerprint")
if not stored_fingerprint:
# New or legacy session - bind it now
request.session["device_fingerprint"] = current_fingerprint
elif stored_fingerprint != current_fingerprint:
# Fingerprint mismatch!
from django.contrib.auth import logout
from django.core.exceptions import PermissionDenied
logout(request)
raise PermissionDenied("Session invalid: Device fingerprint mismatch.")
# Store fingerprint on request for downstream use
request.device_fingerprint = current_fingerprint
return None