http_types_rs/security/
mod.rs

1//! HTTP Security Headers.
2//!
3//! # Specifications
4//!
5//! - [W3C Timing-Allow-Origin header](https://w3c.github.io/resource-timing/#sec-timing-allow-origin)
6//!
7//! # Example
8//!
9//! ```
10//! use http_types_rs::{StatusCode, Response};
11//!
12//! let mut res = Response::new(StatusCode::Ok);
13//! http_types_rs::security::default(&mut res);
14// //! assert_eq!(res["X-Content-Type-Options"], "nosniff");
15// //! assert_eq!(res["X-XSS-Protection"], "1; mode=block");
16//! ```
17
18use crate::headers::{HeaderName, HeaderValue, Headers};
19
20mod csp;
21mod strict_transport_security;
22mod timing_allow_origin;
23
24pub use csp::{ContentSecurityPolicy, Source};
25pub use strict_transport_security::StrictTransportSecurity;
26
27#[cfg(feature = "serde")]
28pub use csp::{ReportTo, ReportToEndpoint};
29
30#[doc(inline)]
31pub use timing_allow_origin::TimingAllowOrigin;
32
33/// Apply a set of default protections.
34///
35// /// ## Examples
36// /// ```
37// /// use http_types_rs::Response;
38// ///
39// /// let mut res = Response::new(StatusCode::Ok);
40// /// http_types_rs::security::default(&mut headers);
41// /// assert_eq!(headers["X-Content-Type-Options"], "nosniff");
42// /// assert_eq!(headers["X-XSS-Protection"], "1; mode=block");
43// /// ```
44pub fn default(mut headers: impl AsMut<Headers>) {
45    dns_prefetch_control(&mut headers);
46    nosniff(&mut headers);
47    frameguard(&mut headers, None);
48    powered_by(&mut headers, None);
49    hsts(&mut headers);
50    xss_filter(&mut headers);
51}
52
53/// Disable browsers’ DNS prefetching by setting the `X-DNS-Prefetch-Control` header.
54///
55/// [read more](https://helmetjs.github.io/docs/dns-prefetch-control/)
56///
57// /// ## Examples
58// /// ```
59// /// use http_types_rs::Response;
60// ///
61// /// let mut res = Response::new(StatusCode::Ok);
62// /// http_types_rs::security::dns_prefetch_control(&mut headers);
63// /// assert_eq!(headers["X-DNS-Prefetch-Control"], "on");
64// /// ```
65#[inline]
66pub fn dns_prefetch_control(mut headers: impl AsMut<Headers>) {
67    // This will never fail, could use an unsafe version of insert.
68    headers.as_mut().insert("X-DNS-Prefetch-Control", "on").unwrap();
69}
70
71/// Set the frameguard level.
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
73pub enum FrameOptions {
74    /// Set to `sameorigin`
75    SameOrigin,
76    /// Set to `deny`
77    Deny,
78}
79
80/// Mitigates clickjacking attacks by setting the `X-Frame-Options` header.
81///
82/// [read more](https://helmetjs.github.io/docs/frameguard/)
83///
84// /// ## Examples
85// /// ```
86// /// use http_types_rs::Response;
87// ///
88// /// let mut res = Response::new(StatusCode::Ok);
89// /// http_types_rs::security::frameguard(&mut headers, None);
90// /// assert_eq!(headers["X-Frame-Options"], "sameorigin");
91// /// ```
92#[inline]
93pub fn frameguard(mut headers: impl AsMut<Headers>, guard: Option<FrameOptions>) {
94    let kind = match guard {
95        None | Some(FrameOptions::SameOrigin) => "sameorigin",
96        Some(FrameOptions::Deny) => "deny",
97    };
98    // This will never fail, could use an unsafe version of insert.
99    headers.as_mut().insert("X-Frame-Options", kind).unwrap();
100}
101
102/// Removes the `X-Powered-By` header to make it slightly harder for attackers to see what
103/// potentially-vulnerable technology powers your site.
104///
105/// [read more](https://helmetjs.github.io/docs/hide-powered-by/)
106///
107// /// ## Examples
108// /// ```
109// /// use http_types_rs::Response;
110// ///
111// /// let mut res = Response::new(StatusCode::Ok);
112// /// headers.as_mut().insert("X-Powered-By", "Tide/Rust".parse());
113// /// http_types_rs::security::hide_powered_by(&mut headers);
114// /// assert_eq!(headers.get("X-Powered-By"), None);
115// /// ```
116#[inline]
117pub fn powered_by(mut headers: impl AsMut<Headers>, value: Option<HeaderValue>) {
118    let name = HeaderName::from_lowercase_str("X-Powered-By");
119    match value {
120        Some(value) => {
121            // Can never fail as value is already a HeaderValue, could use unsafe version of insert
122            headers.as_mut().insert(name, value).unwrap();
123        }
124        None => {
125            headers.as_mut().remove(name);
126        }
127    };
128}
129
130/// Sets the `Strict-Transport-Security` header to keep your users on `HTTPS`.
131///
132/// Note that the header won’t tell users on HTTP to switch to HTTPS, it will tell HTTPS users to
133/// stick around. Defaults to 60 days.
134///
135/// [read more](https://helmetjs.github.io/docs/hsts/)
136///
137// /// ## Examples
138// /// ```
139// /// use http_types_rs::Response;
140// ///
141// /// let mut res = Response::new(StatusCode::Ok);
142// /// http_types_rs::security::hsts(&mut headers);
143// /// assert_eq!(headers["Strict-Transport-Security"], "max-age=5184000");
144// /// ```
145#[inline]
146pub fn hsts(mut headers: impl AsMut<Headers>) {
147    // Never fails, could use unsafe version of insert
148    headers.as_mut().insert("Strict-Transport-Security", "max-age=5184000").unwrap();
149}
150
151/// Prevent browsers from trying to guess (“sniff”) the MIME type, which can have security
152/// implications.
153///
154/// [read more](https://helmetjs.github.io/docs/dont-sniff-mimetype/)
155///
156// /// ## Examples
157// /// ```
158// /// use http_types_rs::Response;
159// ///
160// /// let mut res = Response::new(StatusCode::Ok);
161// /// http_types_rs::security::nosniff(&mut headers);
162// /// assert_eq!(headers["X-Content-Type-Options"], "nosniff");
163// /// ```
164#[inline]
165pub fn nosniff(mut headers: impl AsMut<Headers>) {
166    // Never fails, could use unsafe verison of insert.
167    headers.as_mut().insert("X-Content-Type-Options", "nosniff").unwrap();
168}
169
170/// Sets the `X-XSS-Protection` header to prevent reflected XSS attacks.
171///
172/// [read more](https://helmetjs.github.io/docs/xss-filter/)
173///
174// /// ## Examples
175// /// ```
176// /// use http_types_rs::Response;
177// ///
178// /// let mut res = Response::new(StatusCode::Ok);
179// /// http_types_rs::security::xss_filter(&mut headers);
180// /// assert_eq!(headers["X-XSS-Protection"], "1; mode=block");
181// /// ```
182#[inline]
183pub fn xss_filter(mut headers: impl AsMut<Headers>) {
184    // Never fails, could use unsafe version of insert.
185    headers.as_mut().insert("X-XSS-Protection", "1; mode=block").unwrap();
186}
187
188/// Set the Referrer-Policy level
189#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
190pub enum ReferrerOptions {
191    /// Set to "no-referrer"
192    NoReferrer,
193    /// Set to "no-referrer-when-downgrade" the default
194    NoReferrerDowngrade,
195    /// Set to "same-origin"
196    SameOrigin,
197    /// Set to "origin"
198    Origin,
199    /// Set to "strict-origin"
200    StrictOrigin,
201    /// Set to "origin-when-cross-origin"
202    CrossOrigin,
203    /// Set to "strict-origin-when-cross-origin"
204    StrictCrossOrigin,
205    /// Set to "unsafe-url"
206    UnsafeUrl,
207}
208
209/// Mitigates referrer leakage by controlling the referer\[sic\] header in links away from pages
210///
211/// [read more](https://scotthelme.co.uk/a-new-security-header-referrer-policy/)
212///
213/// [Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy)
214///
215///
216// /// ## Examples
217// /// ```
218// /// use http_types_rs::Response;
219// ///
220// /// let mut res = Response::new(StatusCode::Ok);
221// /// http_types_rs::security::referrer_policy(&mut headers, Some(http_types_rs::security::ReferrerOptions::UnsafeUrl));
222// /// http_types_rs::security::referrer_policy(&mut headers, None);
223// /// let mut referrerValues: Vec<&str> = headers.get_all("Referrer-Policy").iter().map(|x| x.to_str().unwrap()).collect();
224// /// assert_eq!(referrerValues.sort(), vec!("unsafe-url", "no-referrer").sort());
225// /// ```
226#[inline]
227pub fn referrer_policy(mut headers: impl AsMut<Headers>, referrer: Option<ReferrerOptions>) {
228    let policy = match referrer {
229        None | Some(ReferrerOptions::NoReferrer) => "no-referrer",
230        Some(ReferrerOptions::NoReferrerDowngrade) => "no-referrer-when-downgrade",
231        Some(ReferrerOptions::SameOrigin) => "same-origin",
232        Some(ReferrerOptions::Origin) => "origin",
233        Some(ReferrerOptions::StrictOrigin) => "strict-origin",
234        Some(ReferrerOptions::CrossOrigin) => "origin-when-cross-origin",
235        Some(ReferrerOptions::StrictCrossOrigin) => "strict-origin-when-cross-origin",
236        Some(ReferrerOptions::UnsafeUrl) => "unsafe-url",
237    };
238
239    // We MUST allow for multiple Referrer-Policy headers to be set.
240    // See: https://w3c.github.io/webappsec-referrer-policy/#unknown-policy-values example #13
241    // Never fails, could use unsafe version of append.
242    headers.as_mut().append("Referrer-Policy", policy).unwrap();
243}