kanidmd_core/https/middleware/
security_headers.rs

1use axum::{
2    body::Body,
3    extract::State,
4    http::{header, HeaderValue, Request},
5    middleware::Next,
6    response::Response,
7};
8
9use crate::https::ServerState;
10
11const PERMISSIONS_POLICY_VALUE: &str = "fullscreen=(), geolocation=()";
12const X_CONTENT_TYPE_OPTIONS_VALUE: &str = "nosniff";
13
14pub async fn security_headers_layer(
15    State(state): State<ServerState>,
16    request: Request<Body>,
17    next: Next,
18) -> Response {
19    // wait for the middleware to come back
20    let mut response = next.run(request).await;
21
22    // add the Content-Security-Policy header, which defines how contact will be accessed/run based on the source URL
23    let headers = response.headers_mut();
24    headers.insert(header::CONTENT_SECURITY_POLICY, state.csp_header);
25
26    // X-Content-Type-Options tells the browser if it's OK to "sniff" or guess the content type of a response
27    //
28    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
29    // https://scotthelme.co.uk/hardening-your-http-response-headers/#x-content-type-options
30    headers.insert(
31        header::X_CONTENT_TYPE_OPTIONS,
32        HeaderValue::from_static(X_CONTENT_TYPE_OPTIONS_VALUE),
33    );
34
35    // Permissions policy defines access to platform services like geolocation, fullscreen etc.
36    //
37    // https://www.w3.org/TR/permissions-policy-1/
38    headers.insert(
39        "Permissions-Policy",
40        HeaderValue::from_static(PERMISSIONS_POLICY_VALUE),
41    );
42
43    // Don't send a referrer header when the user is navigating to a non-HTTPS URL
44    // Ref:
45    // https://scotthelme.co.uk/a-new-security-header-referrer-policy/
46    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
47    headers.insert(
48        header::REFERRER_POLICY,
49        HeaderValue::from_static("no-referrer-when-downgrade"),
50    );
51
52    response
53}