rustbasic_core/middleware/
security_headers.rs1use crate::requests::Request;
2use crate::middleware::Next;
3use crate::router::Response;
4use http::header;
5
6pub async fn security_headers_middleware(
7 req: Request,
8 next: Next,
9) -> Response {
10 let mut response = next.run(req).await;
11
12 let headers = response.headers_mut();
13
14 headers.insert(header::X_FRAME_OPTIONS, "SAMEORIGIN".parse().unwrap());
16
17 headers.insert(header::X_CONTENT_TYPE_OPTIONS, "nosniff".parse().unwrap());
19
20 headers.insert(header::X_XSS_PROTECTION, "1; mode=block".parse().unwrap());
22
23 headers.insert(header::REFERRER_POLICY, "strict-origin-when-cross-origin".parse().unwrap());
25
26 headers.insert(
28 http::header::HeaderName::from_static("permissions-policy"),
29 "camera=(self), microphone=(self), geolocation=(self), payment=()".parse().unwrap()
30 );
31
32 let cfg = crate::Config::load();
33
34 if !cfg.app_debug {
36 headers.insert(
37 header::STRICT_TRANSPORT_SECURITY,
38 "max-age=31536000; includeSubDomains; preload".parse().unwrap()
39 );
40 }
41 let csp = if cfg.app_debug {
42 let port = cfg.vite_port;
43 let host = &cfg.app_host;
44 let extra_hosts = if host != "0.0.0.0" && host != "127.0.0.1" && host != "localhost" && !host.is_empty() {
45 format!("http://{}:{} ws://{}:{} ", host, port, host, port)
46 } else {
47 "".to_string()
48 };
49
50 format!(
51 "default-src 'self'; \
52 script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:{} http://127.0.0.1:{} {}https:; \
53 style-src 'self' 'unsafe-inline' http://localhost:{} http://127.0.0.1:{} {}https:; \
54 font-src 'self' https: data:; \
55 img-src 'self' data: https:; \
56 frame-src 'self' https:; \
57 media-src 'self' https:; \
58 object-src 'self' https:; \
59 connect-src 'self' ws://localhost:{} ws://127.0.0.1:{} http://localhost:{} http://127.0.0.1:{} {}https:;",
60 port, port, extra_hosts,
61 port, port, extra_hosts,
62 port, port, port, port, extra_hosts
63 )
64 } else {
65 let app_url = &cfg.app_url;
66 let mut extra_connect = String::new();
67 if !app_url.is_empty() {
68 extra_connect.push_str(&format!("{} ", app_url));
69 if app_url.starts_with("https://") {
70 let ws_url = format!("wss://{}", app_url.trim_start_matches("https://"));
71 extra_connect.push_str(&format!("{} ", ws_url));
72 } else if app_url.starts_with("http://") {
73 let ws_url = format!("ws://{}", app_url.trim_start_matches("http://"));
74 extra_connect.push_str(&format!("{} ", ws_url));
75 }
76 }
77 format!(
78 "default-src 'self'; \
79 script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; \
80 style-src 'self' 'unsafe-inline' https:; \
81 font-src 'self' https: data:; \
82 img-src 'self' data: https:; \
83 frame-src 'self' https:; \
84 media-src 'self' https:; \
85 object-src 'self' https:; \
86 connect-src 'self' {}https:;",
87 extra_connect
88 )
89 };
90 headers.insert(header::CONTENT_SECURITY_POLICY, csp.parse().unwrap());
91
92 response
93}