42 lines
1.6 KiB
Rust
42 lines
1.6 KiB
Rust
async fn security_headers(request: Request, next: Next) -> Response {
|
|
let is_api = request.uri().path().contains("/api/");
|
|
let mut response = next.run(request).await;
|
|
let is_html = response
|
|
.headers()
|
|
.get(header::CONTENT_TYPE)
|
|
.and_then(|value| value.to_str().ok())
|
|
.is_some_and(|value| {
|
|
value
|
|
.split(';')
|
|
.next()
|
|
.is_some_and(|mime| mime.trim().eq_ignore_ascii_case("text/html"))
|
|
});
|
|
|
|
let headers = response.headers_mut();
|
|
headers.insert(
|
|
header::HeaderName::from_static("x-content-type-options"),
|
|
HeaderValue::from_static("nosniff"),
|
|
);
|
|
headers.insert(
|
|
header::HeaderName::from_static("referrer-policy"),
|
|
HeaderValue::from_static("same-origin"),
|
|
);
|
|
|
|
if is_html {
|
|
headers.insert(
|
|
header::HeaderName::from_static("x-frame-options"),
|
|
HeaderValue::from_static("SAMEORIGIN"),
|
|
);
|
|
headers.insert(header::HeaderName::from_static("content-security-policy"), HeaderValue::from_static("default-src 'self'; connect-src 'self' ws: wss:; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'; object-src 'none'"));
|
|
headers.insert(
|
|
header::HeaderName::from_static("permissions-policy"),
|
|
HeaderValue::from_static("camera=(), microphone=(), geolocation=()"),
|
|
);
|
|
}
|
|
|
|
if is_api {
|
|
headers.insert(header::CACHE_CONTROL, HeaderValue::from_static("no-store"));
|
|
}
|
|
response
|
|
}
|