From 1bb23b81159c4a63409f1eff2f21a10ee9437a60 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Thu, 8 Nov 2018 09:01:58 -0800 Subject: [PATCH] Rename 'space_helmet' to 'helmet'. Rework API. --- Cargo.toml | 1 - contrib/lib/Cargo.toml | 5 +- contrib/lib/src/helmet/helmet.rs | 212 +++++++++++++ contrib/lib/src/helmet/mod.rs | 100 ++++++ contrib/lib/src/helmet/policy.rs | 401 +++++++++++++++++++++++++ contrib/lib/src/lib.rs | 4 +- contrib/lib/src/space_helmet/helmet.rs | 285 ------------------ contrib/lib/src/space_helmet/mod.rs | 88 ------ contrib/lib/src/space_helmet/policy.rs | 302 ------------------- contrib/lib/tests/helmet.rs | 138 +++++++++ contrib/lib/tests/space_helmet.rs | 99 ------ examples/space_helmet/Cargo.toml | 18 -- examples/space_helmet/Rocket.toml | 4 - examples/space_helmet/private/cert.pem | 30 -- examples/space_helmet/private/key.pem | 51 ---- examples/space_helmet/src/hello.rs | 33 -- scripts/test.sh | 2 +- 17 files changed, 855 insertions(+), 918 deletions(-) create mode 100644 contrib/lib/src/helmet/helmet.rs create mode 100644 contrib/lib/src/helmet/mod.rs create mode 100644 contrib/lib/src/helmet/policy.rs delete mode 100644 contrib/lib/src/space_helmet/helmet.rs delete mode 100644 contrib/lib/src/space_helmet/mod.rs delete mode 100644 contrib/lib/src/space_helmet/policy.rs create mode 100644 contrib/lib/tests/helmet.rs delete mode 100644 contrib/lib/tests/space_helmet.rs delete mode 100644 examples/space_helmet/Cargo.toml delete mode 100644 examples/space_helmet/Rocket.toml delete mode 100644 examples/space_helmet/private/cert.pem delete mode 100644 examples/space_helmet/private/key.pem delete mode 100644 examples/space_helmet/src/hello.rs diff --git a/Cargo.toml b/Cargo.toml index 963cbaae..9f7f11cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ members = [ "core/http/", "contrib/lib", "contrib/codegen", - "examples/space_helmet", "examples/cookies", "examples/errors", "examples/form_validation", diff --git a/contrib/lib/Cargo.toml b/contrib/lib/Cargo.toml index 0e2dd051..ea8921f1 100644 --- a/contrib/lib/Cargo.toml +++ b/contrib/lib/Cargo.toml @@ -21,7 +21,7 @@ json = ["serde", "serde_json"] msgpack = ["serde", "rmp-serde"] tera_templates = ["tera", "templates"] handlebars_templates = ["handlebars", "templates"] -space_helmet = ["time"] +helmet = ["time"] serve = [] # The barage of user-facing database features. @@ -73,8 +73,5 @@ time = { version = "0.1.40", optional = true } [target.'cfg(debug_assertions)'.dependencies] notify = { version = "^4.0" } -[dev-dependencies] -rocket_codegen = { version = "0.4.0-dev", path = "../../core/codegen" } - [package.metadata.docs.rs] all-features = true diff --git a/contrib/lib/src/helmet/helmet.rs b/contrib/lib/src/helmet/helmet.rs new file mode 100644 index 00000000..ab637aa4 --- /dev/null +++ b/contrib/lib/src/helmet/helmet.rs @@ -0,0 +1,212 @@ +use std::collections::HashMap; +use std::sync::atomic::{AtomicBool, Ordering}; + +use rocket::http::uncased::UncasedStr; +use rocket::fairing::{Fairing, Info, Kind}; +use rocket::{Request, Response, Rocket}; + +use helmet::*; + +/// A [`Fairing`](../../rocket/fairing/trait.Fairing.html) that adds HTTP +/// headers to outgoing responses that control security features on the browser. +/// +/// # Usage +/// +/// To use `SpaceHelmet`, first construct an instance of it. To use the default +/// set of headers, construct with [`SpaceHelmet::default()`](#method.default). +/// For an instance with no preset headers, use [`SpaceHelmet::new()`]. To +/// enable an additional header, use [`enable()`](SpaceHelmet::enable()), and to +/// disable a header, use [`disable()`](SpaceHelmet::disable()): +/// +/// ```rust +/// use rocket_contrib::helmet::SpaceHelmet; +/// use rocket_contrib::helmet::{XssFilter, ExpectCt}; +/// +/// // A `SpaceHelmet` with the default headers: +/// let helmet = SpaceHelmet::default(); +/// +/// // A `SpaceHelmet` with the default headers minus `XssFilter`: +/// let helmet = SpaceHelmet::default().disable::(); +/// +/// // A `SpaceHelmet` with the default headers plus `ExpectCt`. +/// let helmet = SpaceHelmet::default().enable(ExpectCt::default()); +/// +/// // A `SpaceHelmet` with only `XssFilter` and `ExpectCt`. +/// let helmet = SpaceHelmet::default() +/// .enable(XssFilter::default()) +/// .enable(ExpectCt::default()); +/// ``` +/// +/// Then, attach the instance of `SpaceHelmet` to your application's instance of +/// `Rocket`: +/// +/// ```rust +/// # extern crate rocket; +/// # extern crate rocket_contrib; +/// # use rocket_contrib::helmet::SpaceHelmet; +/// # let helmet = SpaceHelmet::default(); +/// rocket::ignite() +/// // ... +/// .attach(helmet) +/// # ; +/// ``` +/// +/// The fairing will inject all enabled headers into all outgoing responses +/// _unless_ the response already contains a header with the same name. If it +/// does contain the header, a warning is emitted, and the header is not +/// overwritten. +/// +/// # TLS and HSTS +/// +/// If TLS is configured and enabled when the application is launched in a +/// non-development environment (e.g., staging or production), HSTS is +/// automatically enabled with its default policy and a warning is issued. +/// +/// To get rid of this warning, explicitly [`enable()`](SpaceHelmet::enable()) +/// an [`Hsts`] policy. +pub struct SpaceHelmet { + policies: HashMap<&'static UncasedStr, Box>, + force_hsts: AtomicBool, +} + +impl Default for SpaceHelmet { + /// Returns a new `SpaceHelmet` instance. See the [table] for a description + /// of the policies used by default. + /// + /// [table]: ./#supported-headers + /// + /// # Example + /// + /// ```rust + /// # extern crate rocket; + /// # extern crate rocket_contrib; + /// use rocket_contrib::helmet::SpaceHelmet; + /// + /// let helmet = SpaceHelmet::default(); + /// ``` + fn default() -> Self { + SpaceHelmet::new() + .enable(NoSniff::default()) + .enable(Frame::default()) + .enable(XssFilter::default()) + } +} + +impl SpaceHelmet { + /// Returns an instance of `SpaceHelmet` with no headers enabled. + /// + /// # Example + /// + /// ```rust + /// use rocket_contrib::helmet::SpaceHelmet; + /// + /// let helmet = SpaceHelmet::new(); + /// ``` + pub fn new() -> Self { + SpaceHelmet { + policies: HashMap::new(), + force_hsts: AtomicBool::new(false), + } + } + + /// Enables the policy header `policy`. + /// + /// If the poliicy was previously enabled, the configuration is replaced + /// with that of `policy`. + /// + /// # Example + /// + /// ```rust + /// use rocket_contrib::helmet::SpaceHelmet; + /// use rocket_contrib::helmet::NoSniff; + /// + /// let helmet = SpaceHelmet::new().enable(NoSniff::default()); + /// ``` + pub fn enable(mut self, policy: P) -> Self { + self.policies.insert(P::NAME.into(), Box::new(policy)); + self + } + + /// Disables the policy header `policy`. + /// + /// # Example + /// + /// ```rust + /// use rocket_contrib::helmet::SpaceHelmet; + /// use rocket_contrib::helmet::NoSniff; + /// + /// let helmet = SpaceHelmet::default().disable::(); + /// ``` + pub fn disable(mut self) -> Self { + self.policies.remove(UncasedStr::new(P::NAME)); + self + } + + /// Returns `true` if the policy `P` is enabled. + /// + /// # Example + /// + /// ```rust + /// use rocket_contrib::helmet::SpaceHelmet; + /// use rocket_contrib::helmet::{XssFilter, NoSniff, Frame}; + /// use rocket_contrib::helmet::{Hsts, ExpectCt, Referrer}; + /// + /// let helmet = SpaceHelmet::default(); + /// + /// assert!(helmet.is_enabled::()); + /// assert!(helmet.is_enabled::()); + /// assert!(helmet.is_enabled::()); + /// + /// assert!(!helmet.is_enabled::()); + /// assert!(!helmet.is_enabled::()); + /// assert!(!helmet.is_enabled::()); + /// ``` + pub fn is_enabled(&self) -> bool { + self.policies.contains_key(UncasedStr::new(P::NAME)) + } + + /// Sets all of the headers in `self.policies` in `response` as long as the + /// header is not already in the response. + fn apply(&self, response: &mut Response) { + for policy in self.policies.values() { + let name = policy.name(); + if response.headers().contains(name.as_str()) { + warn!("Space Helmet: response contains a '{}' header.", name); + warn_!("Refusing to overwrite existing header."); + continue + } + + // FIXME: Cache the rendered header. + response.set_header(policy.header()); + } + + if !self.force_hsts.load(Ordering::Relaxed) { + response.set_header(Policy::header(&Hsts::default())); + } + } +} + +impl Fairing for SpaceHelmet { + fn info(&self) -> Info { + Info { + name: "Space Helmet", + kind: Kind::Response | Kind::Launch, + } + } + + fn on_response(&self, _request: &Request, response: &mut Response) { + self.apply(response); + } + + fn on_launch(&self, rocket: &Rocket) { + if rocket.config().tls_enabled() + && !rocket.config().environment.is_dev() + && !self.is_enabled::() + { + warn_!("Space Helmet: deploying with TLS without enabling HSTS."); + warn_!("Enabling default HSTS policy."); + info_!("To disable this warning, configure an HSTS policy."); + self.force_hsts.store(true, Ordering::Relaxed); + } + } +} diff --git a/contrib/lib/src/helmet/mod.rs b/contrib/lib/src/helmet/mod.rs new file mode 100644 index 00000000..65682f7b --- /dev/null +++ b/contrib/lib/src/helmet/mod.rs @@ -0,0 +1,100 @@ +//! Security and privacy headers for all outgoing responses. +//! +//! [`SpaceHelmet`] provides a typed interface for HTTP security headers. It +//! takes some inspiration from [helmetjs], a similar piece of middleware for +//! [express]. +//! +//! [fairing]: https://rocket.rs/v0.4/guide/fairings/ +//! [helmetjs]: https://helmetjs.github.io/ +//! [express]: https://expressjs.com +//! [`SpaceHelmet`]: helmet::SpaceHelmet +//! +//! # Supported Headers +//! +//! | HTTP Header | Description | Policy | Default? | +//! | --------------------------- | -------------------------------------- | ------------ | -------- | +//! | [X-XSS-Protection] | Prevents some reflected XSS attacks. | [`XssFilter`] | ✔ | +//! | [X-Content-Type-Options] | Prevents client sniffing of MIME type. | [`NoSniff`] | ✔ | +//! | [X-Frame-Options] | Prevents [clickjacking]. | [`Frame`] | ✔ | +//! | [Strict-Transport-Security] | Enforces strict use of HTTPS. | [`Hsts`] | ? | +//! | [Expect-CT] | Enables certificate transparency. | [`ExpectCt`] | ✗ | +//! | [Referrer-Policy] | Enables referrer policy. | [`Referrer`] | ✗ | +//! +//! ? If TLS is enabled when the application is launched, in a +//! non-development environment (e.g., staging or production), HSTS is +//! automatically enabled with its default policy and a warning is +//! issued. +//! +//! [X-XSS-Protection]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection +//! [X-Content-Type-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options +//! [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options +//! [Strict-Transport-Security]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security +//! [Expect-CT]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT +//! [Referrer-Policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy +//! [clickjacking]: https://en.wikipedia.org/wiki/Clickjacking +//! +//! [`XssFilter`]: helmet::XssFilter +//! [`NoSniff`]: helmet::NoSniff +//! [`Frame`]: helmet::Frame +//! [`Hsts`]: helmet::Hsts +//! [`ExpectCt`]: helmet::ExpectCt +//! [`Referrer`]: helmet::Referrer +//! +//! # Usage +//! +//! To apply default headers, simply attach an instance of [`SpaceHelmet`] +//! before launching: +//! +//! ```rust +//! # extern crate rocket; +//! # extern crate rocket_contrib; +//! use rocket_contrib::helmet::SpaceHelmet; +//! +//! let rocket = rocket::ignite().attach(SpaceHelmet::default()); +//! ``` +//! +//! Each header can be configured individually. To enable a particular header, +//! call the chainable [`enable()`](helmet::SpaceHelmet::enable()) method +//! on an instance of `SpaceHelmet`, passing in the configured policy type. +//! Similarly, to disable a header, call the chainable +//! [`disable()`](helmet::SpaceHelmet::disable()) method on an instance of +//! `SpaceHelmet`: +//! +//! ```rust +//! # extern crate rocket; +//! # extern crate rocket_contrib; +//! use rocket::http::uri::Uri; +//! use rocket_contrib::helmet::{SpaceHelmet, Frame, XssFilter, Hsts, NoSniff}; +//! +//! let site_uri = Uri::parse("https://mysite.example.com").unwrap(); +//! let report_uri = Uri::parse("https://report.example.com").unwrap(); +//! let helmet = SpaceHelmet::default() +//! .enable(Hsts::default()) +//! .enable(Frame::AllowFrom(site_uri)) +//! .enable(XssFilter::EnableReport(report_uri)) +//! .disable::(); +//! ``` +//! +//! # FAQ +//! +//! * **Which policies should I choose?** +//! +//! See the links in the table above for individual header documentation. The +//! [helmetjs] docs are also a good resource, and [OWASP] has a collection of +//! references on these headers. +//! +//! * **Do I need any headers beyond what `SpaceHelmet` enables by default?** +//! +//! Maybe! The other headers can protect against many important +//! vulnerabilities. Please consult their documentation and other resources to +//! determine if they are needed for your project. +//! +//! [OWASP]: https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#tab=Headers + +extern crate time; + +mod helmet; +mod policy; + +pub use self::helmet::SpaceHelmet; +pub use self::policy::*; diff --git a/contrib/lib/src/helmet/policy.rs b/contrib/lib/src/helmet/policy.rs new file mode 100644 index 00000000..bb40532e --- /dev/null +++ b/contrib/lib/src/helmet/policy.rs @@ -0,0 +1,401 @@ +//! Module containing the [`Policy`] trait and types that implement it. + +use std::borrow::Cow; + +use rocket::http::{Header, uri::Uri, uncased::UncasedStr}; + +use helmet::time::Duration; + +/// Trait implemented by security and privacy policy headers. +/// +/// Types that implement this trait can be [`enable()`]d and [`disable()`]d on +/// instances of [`SpaceHelmet`]. +/// +/// [`SpaceHelmet`]: ::helmet::SpaceHelmet +/// [`enable()`]: ::helmet::SpaceHelmet::enable() +/// [`disable()`]: ::helmet::SpaceHelmet::disable() +pub trait Policy: Default + Send + Sync + 'static { + /// The actual name of the HTTP header. + /// + /// This name must uniquely identify the header as it is used to determine + /// whether two implementations of `Policy` are for the same header. Use the + /// real HTTP header's name. + /// + /// # Example + /// + /// ```rust + /// # extern crate rocket; + /// # extern crate rocket_contrib; + /// # use rocket::http::Header; + /// use rocket_contrib::helmet::Policy; + /// + /// #[derive(Default)] + /// struct MyPolicy; + /// + /// impl Policy for MyPolicy { + /// const NAME: &'static str = "X-My-Policy"; + /// # fn header(&self) -> Header<'static> { unimplemented!() } + /// } + /// ``` + const NAME: &'static str; + + /// Returns the [`Header`](../../rocket/http/struct.Header.html) to attach + /// to all outgoing responses. + /// + /// # Example + /// + /// ```rust + /// # extern crate rocket; + /// # extern crate rocket_contrib; + /// use rocket::http::Header; + /// use rocket_contrib::helmet::Policy; + /// + /// #[derive(Default)] + /// struct MyPolicy; + /// + /// impl Policy for MyPolicy { + /// # const NAME: &'static str = "X-My-Policy"; + /// fn header(&self) -> Header<'static> { + /// Header::new(Self::NAME, "value-to-enable") + /// } + /// } + /// ``` + fn header(&self) -> Header<'static>; +} + +crate trait SubPolicy: Send + Sync { + fn name(&self) -> &'static UncasedStr; + fn header(&self) -> Header<'static>; +} + +impl SubPolicy for P { + fn name(&self) -> &'static UncasedStr { + P::NAME.into() + } + + fn header(&self) -> Header<'static> { + Policy::header(self) + } +} + +macro_rules! impl_policy { + ($T:ty, $name:expr) => ( + impl Policy for $T { + const NAME: &'static str = $name; + + fn header(&self) -> Header<'static> { + self.into() + } + } + ) +} + +impl_policy!(XssFilter, "X-XSS-Protection"); +impl_policy!(NoSniff, "X-Content-Type-Options"); +impl_policy!(Frame, "X-Frame-Options"); +impl_policy!(Hsts, "Strict-Transport-Security"); +impl_policy!(ExpectCt, "Expect-CT"); +impl_policy!(Referrer, "Referrer-Policy"); + +/// The [Referrer-Policy] header: controls the value set by the browser for the +/// [Referer] header. +/// +/// Tells the browser if it should send all or part of URL of the current page +/// to the next site the user navigates to via the [Referer] header. This can be +/// important for security as the URL itself might expose sensitive data, such +/// as a hidden file path or personal identifier. +/// +/// [Referrer-Policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy +/// [Referer]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer +pub enum Referrer { + /// Omits the `Referer` header (_SpaceHelmet default_). + NoReferrer, + + /// Omits the `Referer` header on connection downgrade i.e. following HTTP + /// link from HTTPS site (_Browser default_). + NoReferrerWhenDowngrade, + + /// Only send the origin of part of the URL, e.g. the origin of + /// https://foo.com/bob.html is https://foo.com + Origin, + + /// Send full URL for same-origin requests, only send origin part when + /// replying to [cross-origin] requests. + /// + /// [cross-origin]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS + OriginWhenCrossOrigin, + + /// Send full URL for same-origin requests only. + SameOrigin, + + /// Only send origin part of URL, only send if protocol security level + /// remains the same e.g. HTTPS to HTTPS. + StrictOrigin, + + /// Send full URL for same-origin requests. For cross-origin requests, only + /// send origin part of URL if protocl security level remains the same e.g. + /// HTTPS to HTTPS. + StrictOriginWhenCrossOrigin, + + /// Send full URL for same-origin or cross-origin requests. _This will leak + /// the full URL of TLS protected resources to insecure origins. Use with + /// caution._ + UnsafeUrl, + } + +/// Defaults to [`Referrer::NoReferrer`]. Tells the browser to omit the +/// `Referer` header. +impl Default for Referrer { + fn default() -> Referrer { + Referrer::NoReferrer + } +} + +impl<'h, 'a> Into> for &'a Referrer { + fn into(self) -> Header<'h> { + let policy_string = match self { + Referrer::NoReferrer => "no-referrer", + Referrer::NoReferrerWhenDowngrade => "no-referrer-when-downgrade", + Referrer::Origin => "origin", + Referrer::OriginWhenCrossOrigin => "origin-when-cross-origin", + Referrer::SameOrigin => "same-origin", + Referrer::StrictOrigin => "strict-origin", + Referrer::StrictOriginWhenCrossOrigin => "strict-origin-when-cross-origin", + Referrer::UnsafeUrl => "unsafe-url", + }; + + Header::new(Referrer::NAME, policy_string) + } +} + +/// The [Expect-CT] header: enables [Certificate Transparency] to detect and +/// prevent misuse of TLS certificates. +/// +/// [Certificate Transparency] solves a variety of problems with public TLS/SSL +/// certificate management and is valuable measure for all public applications. +/// If you're just [getting started] with certificate transparency, ensure that +/// your [site is in compliance][getting started] before you enable enforcement +/// with [`ExpectCt::Enforce`] or [`ExpectCt::ReportAndEnforce`]. Failure to do +/// so will result in the browser refusing to communicate with your application. +/// _You have been warned_. +/// +/// [Expect-CT]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT +/// [Certificate Transparency]: http://www.certificate-transparency.org/what-is-ct +/// [getting started]: http://www.certificate-transparency.org/getting-started +pub enum ExpectCt { + /// Enforce certificate compliance for the next [`Duration`]. Ensure that + /// your certificates are in compliance before turning on enforcement. + /// (_SpaceHelmet_ default). + Enforce(Duration), + + /// Report to `Uri`, but do not enforce, compliance violations for the next + /// [`Duration`]. Doesn't provide any protection but is a good way make sure + /// things are working correctly before turning on enforcement in + /// production. + Report(Duration, Uri<'static>), + + /// Enforce compliance and report violations to `Uri` for the next + /// [`Duration`]. + ReportAndEnforce(Duration, Uri<'static>), +} + +/// Defaults to [`ExpectCt::Enforce(Duration::days(30))`], enforce CT +/// compliance, see [draft] standard for more. +/// +/// [draft]: https://tools.ietf.org/html/draft-ietf-httpbis-expect-ct-03#page-15 +impl Default for ExpectCt { + fn default() -> ExpectCt { + ExpectCt::Enforce(Duration::days(30)) + } +} + +impl<'a> Into> for &'a ExpectCt { + fn into(self) -> Header<'static> { + let policy_string = match self { + ExpectCt::Enforce(age) => format!("max-age={}, enforce", age.num_seconds()), + ExpectCt::Report(age, uri) => { + format!(r#"max-age={}, report-uri="{}""#, age.num_seconds(), uri) + } + ExpectCt::ReportAndEnforce(age, uri) => { + format!("max-age={}, enforce, report-uri=\"{}\"", age.num_seconds(), uri) + } + }; + + Header::new(ExpectCt::NAME, policy_string) + } +} + +/// The [X-Content-Type-Options] header: turns off [mime sniffing] which can +/// prevent certain [attacks]. +/// +/// [mime sniffing]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#MIME_sniffing +/// [X-Content-Type-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options +/// [attacks]: https://helmetjs.github.io/docs/dont-sniff-mimetype/ +pub enum NoSniff { + /// Turns off mime sniffing. + Enable, +} + +/// Defaults to [`NoSniff::Enable`], turns off mime sniffing. +impl Default for NoSniff { + fn default() -> NoSniff { + NoSniff::Enable + } +} + +impl<'h, 'a> Into> for &'a NoSniff { + fn into(self) -> Header<'h> { + Header::new(NoSniff::NAME, "nosniff") + } +} + +/// The HTTP [Strict-Transport-Security] (HSTS) header: enforces strict HTTPS +/// usage. +/// +/// HSTS tells the browser that the site should only be accessed using HTTPS +/// instead of HTTP. HSTS prevents a variety of downgrading attacks and should +/// always be used when TLS is enabled. `SpaceHelmet` will turn HSTS on and +/// issue a warning if you enable TLS without enabling HSTS when the application +/// is run in the staging or production environments. +/// +/// While HSTS is important for HTTPS security, incorrectly configured HSTS can +/// lead to problems as you are disallowing access to non-HTTPS enabled parts of +/// your site. [Yelp engineering] has good discussion of potential challenges +/// that can arise and how to roll this out in a large scale setting. So, if +/// you use TLS, use HSTS, but roll it out with care. +/// +/// [TLS]: https://rocket.rs/guide/configuration/#configuring-tls +/// [Strict-Transport-Security]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security +/// [default policy]: /rocket_contrib/helmet/enum.Hsts.html#impl-Default +/// [Yelp engineering]: https://engineeringblog.yelp.com/2017/09/the-road-to-hsts.html +/// [Staging]: /rocket/config/enum.Environment.html#variant.Staging +/// [Production]: /rocket/config/enum.Environment.html#variant.Production +pub enum Hsts { + /// Browser should only permit this site to be accesses by HTTPS for the + /// next [`Duration`]. + Enable(Duration), + + /// Like [`Hsts::Enable`], but also apply to all of the site's subdomains. + IncludeSubDomains(Duration), + + /// Google maintains an [HSTS preload service] that can be used to prevent + /// the browser from ever connecting to your site over an insecure + /// connection. Read more [here]. Don't enable this before you have + /// registered your site. + /// + /// [HSTS preload service]: https://hstspreload.org/ + /// [here]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#Preloading_Strict_Transport_Security + Preload(Duration), +} + +/// Defaults to `Hsts::Enable(Duration::weeks(52))`. +impl Default for Hsts { + fn default() -> Hsts { + Hsts::Enable(Duration::weeks(52)) + } +} + +impl<'a> Into> for &'a Hsts { + fn into(self) -> Header<'static> { + let policy_string = match self { + Hsts::Enable(age) => format!("max-age={}", age.num_seconds()), + Hsts::IncludeSubDomains(age) => { + format!("max-age={}; includeSubDomains", age.num_seconds()) + } + Hsts::Preload(age) => format!("max-age={}; preload", age.num_seconds()), + }; + + Header::new(Hsts::NAME, policy_string) + } +} + +/// The [X-Frame-Options] header: helps prevent [clickjacking] attacks. +/// +/// Controls whether the browser should allow the page to render in a ``, +/// [`