use axum::{
body::Body,
http::{header::HeaderValue, Request, Response},
middleware::Next,
};
pub async fn security_headers_middleware(request: Request<Body>, next: Next) -> Response<Body> {
let mut response = next.run(request).await;
let headers = response.headers_mut();
headers.insert(
"x-content-type-options",
HeaderValue::from_static("nosniff"),
);
headers.insert("x-frame-options", HeaderValue::from_static("DENY"));
headers.insert("x-xss-protection", HeaderValue::from_static("0"));
headers.insert(
"referrer-policy",
HeaderValue::from_static("strict-origin-when-cross-origin"),
);
headers.insert(
"content-security-policy",
HeaderValue::from_static("default-src 'none'; frame-ancestors 'none'"),
);
headers.insert("cache-control", HeaderValue::from_static("no-store"));
response
}
#[cfg(test)]
mod tests {
use super::*;
use axum::{routing::get, Router};
use tower::ServiceExt;
async fn ok_handler() -> &'static str {
"ok"
}
fn test_router() -> Router {
Router::new()
.route("/test", get(ok_handler))
.layer(axum::middleware::from_fn(security_headers_middleware))
}
#[tokio::test]
async fn test_security_headers_present() {
let router = test_router();
let request = Request::builder().uri("/test").body(Body::empty()).unwrap();
let response = router.oneshot(request).await.unwrap();
let headers = response.headers();
assert_eq!(headers.get("x-content-type-options").unwrap(), "nosniff");
assert_eq!(headers.get("x-frame-options").unwrap(), "DENY");
assert_eq!(headers.get("x-xss-protection").unwrap(), "0");
assert_eq!(
headers.get("referrer-policy").unwrap(),
"strict-origin-when-cross-origin"
);
assert_eq!(
headers.get("content-security-policy").unwrap(),
"default-src 'none'; frame-ancestors 'none'"
);
assert_eq!(headers.get("cache-control").unwrap(), "no-store");
}
}