security: harden admin password change and production session headers
This commit is contained in:
36
app.py
36
app.py
@@ -135,6 +135,33 @@ def _is_api_or_health_path(path: str) -> bool:
|
||||
return raw.startswith("/api/") or raw.startswith("/yuyx/api/") or raw == "/health"
|
||||
|
||||
|
||||
def _request_uses_https() -> bool:
|
||||
try:
|
||||
if bool(request.is_secure):
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
forwarded_proto = str(request.headers.get("X-Forwarded-Proto", "") or "").split(",", 1)[0].strip().lower()
|
||||
if forwarded_proto == "https":
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
|
||||
_SECURITY_RESPONSE_HEADERS = {
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"X-Frame-Options": "SAMEORIGIN",
|
||||
"Referrer-Policy": "strict-origin-when-cross-origin",
|
||||
"Permissions-Policy": "camera=(), microphone=(), geolocation=(), payment=()",
|
||||
}
|
||||
|
||||
_SECURITY_CSP_HEADER = str(os.environ.get("SECURITY_CONTENT_SECURITY_POLICY", "") or "").strip()
|
||||
|
||||
|
||||
_HASHED_STATIC_ASSET_RE = re.compile(r".*-[a-z0-9_-]{8,}\.(?:js|css|woff2?|ttf|svg|png|jpe?g|webp)$", re.IGNORECASE)
|
||||
|
||||
|
||||
@@ -238,6 +265,15 @@ def ensure_csrf_cookie(response):
|
||||
samesite=config.SESSION_COOKIE_SAMESITE,
|
||||
)
|
||||
|
||||
for header_name, header_value in _SECURITY_RESPONSE_HEADERS.items():
|
||||
response.headers.setdefault(header_name, header_value)
|
||||
|
||||
if _request_uses_https():
|
||||
response.headers.setdefault("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
|
||||
|
||||
if _SECURITY_CSP_HEADER:
|
||||
response.headers.setdefault("Content-Security-Policy", _SECURITY_CSP_HEADER)
|
||||
|
||||
_record_request_metric_after_response(response)
|
||||
return response
|
||||
|
||||
|
||||
Reference in New Issue
Block a user