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 XXSSProtection {
52 Disable,
53 Enable,
54 EnableBlock,
55 EnableReport(String),
56}
57
58impl Display for XXSSProtection {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 match self {
61 XXSSProtection::Disable => f.write_str("0"),
62 XXSSProtection::Enable => f.write_str("1"),
63 XXSSProtection::EnableBlock => f.write_str("1; mode=block"),
64 XXSSProtection::EnableReport(x) => f.write_str(&format!("1; report={}", x)),
65 }
66 }
67}
68
69#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
70#[derive(Clone, Debug)]
71pub enum CrossOriginOpenerPolicy {
72 UnsafeNone,
73 SameOriginAllowPopups,
74 SameOrigin,
75}
76
77impl Display for CrossOriginOpenerPolicy {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 f.write_str(match self {
80 CrossOriginOpenerPolicy::UnsafeNone => "unsafe-none",
81 CrossOriginOpenerPolicy::SameOriginAllowPopups => "same-origin-allow-popups",
82 CrossOriginOpenerPolicy::SameOrigin => "same-origin",
83 })
84 }
85}
86
87#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88#[derive(Clone, Debug)]
89pub enum StrictTransportSecurity {
90 MaxAge(u32),
91 IncludeSubDomains(u32),
92 Preload(u32),
93}
94
95impl Display for StrictTransportSecurity {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 match self {
98 StrictTransportSecurity::MaxAge(x) => f.write_str(&format!("max-age={}", x)),
99 StrictTransportSecurity::IncludeSubDomains(x) => {
100 f.write_str(&format!("max-age={}; includeSubDomains", x))
101 }
102 StrictTransportSecurity::Preload(x) => {
103 f.write_str(&format!("max-age={}; includeSubDomains; preload", x))
104 }
105 }
106 }
107}
108
109#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
110#[derive(Clone, Debug)]
111pub struct SecurityHeader {
112 pub referer_policy: RefererPolicy,
113 pub x_frame_options: XFrameOptions,
114 pub x_xss_protection: XXSSProtection,
115 pub cross_origin_opener_policy: CrossOriginOpenerPolicy,
116 pub content_security_policy: String,
117 pub strict_transport_security: Option<StrictTransportSecurity>,
118}
119
120impl Default for SecurityHeader {
121 fn default() -> Self {
122 Self {
123 referer_policy: RefererPolicy::StrictOriginWhenCrossOrigin,
124 x_frame_options: XFrameOptions::Deny,
125 x_xss_protection: XXSSProtection::EnableBlock,
126 cross_origin_opener_policy: CrossOriginOpenerPolicy::SameOrigin,
127 content_security_policy: String::from("default-src 'none'; script-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'"),
128 strict_transport_security: None,
129 }
130 }
131}
132
133impl SecurityHeader {
134 pub fn set_default_hsts(&mut self) {
138 self.strict_transport_security = Some(StrictTransportSecurity::Preload(31536000));
139 }
140
141 pub fn build(self) -> middleware::DefaultHeaders {
142 let mut ret = middleware::DefaultHeaders::new()
143 .add(("X-Content-Type-Options", "nosniff"))
144 .add(("Referrer-Policy", self.referer_policy.to_string()))
145 .add(("X-Frame-Options", self.x_frame_options.to_string()))
146 .add(("X-XSS-Protection", self.x_xss_protection.to_string()))
147 .add((
148 "Cross-Origin-Opener-Policy",
149 self.cross_origin_opener_policy.to_string(),
150 ))
151 .add(("Content-Security-Policy", self.content_security_policy));
152 if let Some(hsts) = self.strict_transport_security {
153 ret = ret.add(("Strict-Transport-Security", hsts.to_string()));
154 }
155 ret
156 }
157}