use axum::extract::Request;
use axum::http::HeaderValue;
use axum::http::header::{
CONTENT_SECURITY_POLICY, HeaderName, REFERRER_POLICY, STRICT_TRANSPORT_SECURITY,
X_CONTENT_TYPE_OPTIONS, X_FRAME_OPTIONS,
};
use axum::middleware::Next;
use axum::response::Response;
pub const DEFAULT_CSP: &str = "default-src 'self'; \
script-src 'self'; \
style-src 'self' 'unsafe-inline'; \
font-src 'self' data:; \
img-src 'self' data:; \
object-src 'none'; \
base-uri 'self'; \
frame-ancestors 'none'";
const HSTS_VALUE: &str = "max-age=63072000; includeSubDomains";
pub async fn security_headers(request: Request, next: Next) -> Response {
let scheme_is_https = request.uri().scheme_str() == Some("https");
let mut response = next.run(request).await;
let headers = response.headers_mut();
if !headers.contains_key(X_CONTENT_TYPE_OPTIONS) {
headers.insert(X_CONTENT_TYPE_OPTIONS, HeaderValue::from_static("nosniff"));
}
if !headers.contains_key(X_FRAME_OPTIONS) {
headers.insert(X_FRAME_OPTIONS, HeaderValue::from_static("DENY"));
}
if !headers.contains_key(REFERRER_POLICY) {
headers.insert(REFERRER_POLICY, HeaderValue::from_static("no-referrer"));
}
let permissions_policy: HeaderName = HeaderName::from_static("permissions-policy");
if !headers.contains_key(&permissions_policy) {
headers.insert(
permissions_policy,
HeaderValue::from_static(
"accelerometer=(), camera=(), geolocation=(), gyroscope=(), \
magnetometer=(), microphone=(), payment=(), usb=()",
),
);
}
if scheme_is_https && !headers.contains_key(STRICT_TRANSPORT_SECURITY) {
headers.insert(
STRICT_TRANSPORT_SECURITY,
HeaderValue::from_static(HSTS_VALUE),
);
}
if !headers.contains_key(CONTENT_SECURITY_POLICY) {
headers.insert(
CONTENT_SECURITY_POLICY,
HeaderValue::from_static(DEFAULT_CSP),
);
}
response
}