1use std::fmt::Display;
2
3use actix_web::middleware;
4
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6#[derive(Clone, Debug)]
7pub enum RefererPolicy {
8 NoReferrer,
9 NoReferrerWhenDowngrade,
10 Origin,
11 OriginWhenCrossOrigin,
12 SameOrigin,
13 StrictOrigin,
14 StrictOriginWhenCrossOrigin,
15 UnsafeUrl,
16}
17
18impl Display for RefererPolicy {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 f.write_str(match self {
21 RefererPolicy::NoReferrer => "no-referrer",
22 RefererPolicy::NoReferrerWhenDowngrade => "no-referrer-when-downgrade",
23 RefererPolicy::Origin => "origin",
24 RefererPolicy::OriginWhenCrossOrigin => "origin-when-cross-origin",
25 RefererPolicy::SameOrigin => "same-origin",
26 RefererPolicy::StrictOrigin => "strict-origin",
27 RefererPolicy::StrictOriginWhenCrossOrigin => "strict-origin-when-cross-origin",
28 RefererPolicy::UnsafeUrl => "unsafe-url",
29 })
30 }
31}
32
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34#[derive(Clone, Debug)]
35pub enum XFrameOptions {
36 Deny,
37 SameOrigin,
38}
39
40impl Display for XFrameOptions {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 f.write_str(match self {
43 XFrameOptions::Deny => "DENY",
44 XFrameOptions::SameOrigin => "SAMEORIGIN",
45 })
46 }
47}
48
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50#[derive(Clone, Debug)]
51pub enum CrossOriginOpenerPolicy {
52 UnsafeNone,
53 SameOriginAllowPopups,
54 SameOrigin,
55}
56
57impl Display for CrossOriginOpenerPolicy {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 f.write_str(match self {
60 CrossOriginOpenerPolicy::UnsafeNone => "unsafe-none",
61 CrossOriginOpenerPolicy::SameOriginAllowPopups => "same-origin-allow-popups",
62 CrossOriginOpenerPolicy::SameOrigin => "same-origin",
63 })
64 }
65}
66
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68#[derive(Clone, Debug)]
69pub enum StrictTransportSecurity {
70 MaxAge(u32),
71 IncludeSubDomains(u32),
72 Preload(u32),
73}
74
75impl Display for StrictTransportSecurity {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 match self {
78 StrictTransportSecurity::MaxAge(x) => f.write_str(&format!("max-age={}", x)),
79 StrictTransportSecurity::IncludeSubDomains(x) => {
80 f.write_str(&format!("max-age={}; includeSubDomains", x))
81 }
82 StrictTransportSecurity::Preload(x) => {
83 f.write_str(&format!("max-age={}; includeSubDomains; preload", x))
84 }
85 }
86 }
87}
88
89#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
90#[derive(Clone, Debug)]
91pub struct SecurityHeader {
92 pub referer_policy: RefererPolicy,
93 pub x_frame_options: XFrameOptions,
94 pub cross_origin_opener_policy: CrossOriginOpenerPolicy,
95 pub content_security_policy: String,
96 pub strict_transport_security: Option<StrictTransportSecurity>,
97}
98
99impl Default for SecurityHeader {
100 fn default() -> Self {
101 Self {
102 referer_policy: RefererPolicy::StrictOriginWhenCrossOrigin,
103 x_frame_options: XFrameOptions::Deny,
104 cross_origin_opener_policy: CrossOriginOpenerPolicy::SameOrigin,
105 content_security_policy: String::from("default-src 'none'; script-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'"),
106 strict_transport_security: None,
107 }
108 }
109}
110
111impl SecurityHeader {
112 pub fn set_default_hsts(&mut self) {
116 self.strict_transport_security = Some(StrictTransportSecurity::Preload(31536000));
117 }
118
119 pub fn build(self) -> middleware::DefaultHeaders {
120 let mut ret = middleware::DefaultHeaders::new()
121 .add(("X-Content-Type-Options", "nosniff"))
122 .add(("Referrer-Policy", self.referer_policy.to_string()))
123 .add(("X-Frame-Options", self.x_frame_options.to_string()))
124 .add((
125 "Cross-Origin-Opener-Policy",
126 self.cross_origin_opener_policy.to_string(),
127 ))
128 .add(("Content-Security-Policy", self.content_security_policy));
129 if let Some(hsts) = self.strict_transport_security {
130 ret = ret.add(("Strict-Transport-Security", hsts.to_string()));
131 }
132 ret
133 }
134}