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