Spaces:
Running
Running
File size: 4,581 Bytes
fb22f47 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
import re
import os
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
from typing import Dict
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
response = await call_next(request)
response.headers.update(set_security_headers())
return response
def set_security_headers() -> Dict[str, str]:
"""
Sets security headers based on environment variables.
This function reads specific environment variables and uses their values
to set corresponding security headers. The headers that can be set are:
- cache-control
- permissions-policy
- strict-transport-security
- referrer-policy
- x-content-type-options
- x-download-options
- x-frame-options
- x-permitted-cross-domain-policies
- content-security-policy
Each environment variable is associated with a specific setter function
that constructs the header. If the environment variable is set, the
corresponding header is added to the options dictionary.
Returns:
dict: A dictionary containing the security headers and their values.
"""
options = {}
header_setters = {
"CACHE_CONTROL": set_cache_control,
"HSTS": set_hsts,
"PERMISSIONS_POLICY": set_permissions_policy,
"REFERRER_POLICY": set_referrer,
"XCONTENT_TYPE": set_xcontent_type,
"XDOWNLOAD_OPTIONS": set_xdownload_options,
"XFRAME_OPTIONS": set_xframe,
"XPERMITTED_CROSS_DOMAIN_POLICIES": set_xpermitted_cross_domain_policies,
"CONTENT_SECURITY_POLICY": set_content_security_policy,
}
for env_var, setter in header_setters.items():
value = os.environ.get(env_var, None)
if value:
header = setter(value)
if header:
options.update(header)
return options
# Set HTTP Strict Transport Security(HSTS) response header
def set_hsts(value: str):
pattern = r"^max-age=(\d+)(;includeSubDomains)?(;preload)?$"
match = re.match(pattern, value, re.IGNORECASE)
if not match:
value = "max-age=31536000;includeSubDomains"
return {"Strict-Transport-Security": value}
# Set X-Frame-Options response header
def set_xframe(value: str):
pattern = r"^(DENY|SAMEORIGIN)$"
match = re.match(pattern, value, re.IGNORECASE)
if not match:
value = "DENY"
return {"X-Frame-Options": value}
# Set Permissions-Policy response header
def set_permissions_policy(value: str):
pattern = r"^(?:(accelerometer|autoplay|camera|clipboard-read|clipboard-write|fullscreen|geolocation|gyroscope|magnetometer|microphone|midi|payment|picture-in-picture|sync-xhr|usb|xr-spatial-tracking)=\((self)?\),?)*$"
match = re.match(pattern, value, re.IGNORECASE)
if not match:
value = "none"
return {"Permissions-Policy": value}
# Set Referrer-Policy response header
def set_referrer(value: str):
pattern = r"^(no-referrer|no-referrer-when-downgrade|origin|origin-when-cross-origin|same-origin|strict-origin|strict-origin-when-cross-origin|unsafe-url)$"
match = re.match(pattern, value, re.IGNORECASE)
if not match:
value = "no-referrer"
return {"Referrer-Policy": value}
# Set Cache-Control response header
def set_cache_control(value: str):
pattern = r"^(public|private|no-cache|no-store|must-revalidate|proxy-revalidate|max-age=\d+|s-maxage=\d+|no-transform|immutable)(,\s*(public|private|no-cache|no-store|must-revalidate|proxy-revalidate|max-age=\d+|s-maxage=\d+|no-transform|immutable))*$"
match = re.match(pattern, value, re.IGNORECASE)
if not match:
value = "no-store, max-age=0"
return {"Cache-Control": value}
# Set X-Download-Options response header
def set_xdownload_options(value: str):
if value != "noopen":
value = "noopen"
return {"X-Download-Options": value}
# Set X-Content-Type-Options response header
def set_xcontent_type(value: str):
if value != "nosniff":
value = "nosniff"
return {"X-Content-Type-Options": value}
# Set X-Permitted-Cross-Domain-Policies response header
def set_xpermitted_cross_domain_policies(value: str):
pattern = r"^(none|master-only|by-content-type|by-ftp-filename)$"
match = re.match(pattern, value, re.IGNORECASE)
if not match:
value = "none"
return {"X-Permitted-Cross-Domain-Policies": value}
# Set Content-Security-Policy response header
def set_content_security_policy(value: str):
return {"Content-Security-Policy": value}
|