Skip to main content

actix_cloud/
security.rs

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    /// Set default HSTS to 1 year, includeSubDomains and preload.
113    ///
114    /// `max-age=31536000; includeSubDomains; preload`
115    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}