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    let headers = response.headers_mut();
23
24    // add the Content-Security-Policy header, which defines how contact will be accessed/run based on the source URL
25    // Only update the CSP if none is present. This allows some routes to opt into defining their own CSP
26    if !headers.contains_key(header::CONTENT_SECURITY_POLICY) {
27        headers.insert(header::CONTENT_SECURITY_POLICY, state.csp_header);
28    }
29
30    // X-Content-Type-Options tells the browser if it's OK to "sniff" or guess the content type of a response
31    //
32    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
33    // https://scotthelme.co.uk/hardening-your-http-response-headers/#x-content-type-options
34    headers.insert(
35        header::X_CONTENT_TYPE_OPTIONS,
36        HeaderValue::from_static(X_CONTENT_TYPE_OPTIONS_VALUE),
37    );
38
39    // Permissions policy defines access to platform services like geolocation, fullscreen etc.
40    //
41    // https://www.w3.org/TR/permissions-policy-1/
42    headers.insert(
43        "Permissions-Policy",
44        HeaderValue::from_static(PERMISSIONS_POLICY_VALUE),
45    );
46
47    // Don't send a referrer header when the user is navigating to a non-HTTPS URL
48    // Ref:
49    // https://scotthelme.co.uk/a-new-security-header-referrer-policy/
50    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
51    headers.insert(
52        header::REFERRER_POLICY,
53        HeaderValue::from_static("no-referrer-when-downgrade"),
54    );
55
56    response
57}
58
59pub async fn csp_header_no_form_action_layer(
60    State(state): State<ServerState>,
61    request: Request<Body>,
62    next: Next,
63) -> Response {
64    let mut response = next.run(request).await;
65
66    let headers = response.headers_mut();
67
68    headers.insert(
69        header::CONTENT_SECURITY_POLICY,
70        state.csp_header_no_form_action,
71    );
72
73    response
74}