mirror of https://github.com/rwf2/Rocket.git
Rename 'space_helmet' to 'helmet'. Rework API.
This commit is contained in:
parent
c5167f1150
commit
1bb23b8115
|
@ -8,7 +8,6 @@ members = [
|
||||||
"core/http/",
|
"core/http/",
|
||||||
"contrib/lib",
|
"contrib/lib",
|
||||||
"contrib/codegen",
|
"contrib/codegen",
|
||||||
"examples/space_helmet",
|
|
||||||
"examples/cookies",
|
"examples/cookies",
|
||||||
"examples/errors",
|
"examples/errors",
|
||||||
"examples/form_validation",
|
"examples/form_validation",
|
||||||
|
|
|
@ -21,7 +21,7 @@ json = ["serde", "serde_json"]
|
||||||
msgpack = ["serde", "rmp-serde"]
|
msgpack = ["serde", "rmp-serde"]
|
||||||
tera_templates = ["tera", "templates"]
|
tera_templates = ["tera", "templates"]
|
||||||
handlebars_templates = ["handlebars", "templates"]
|
handlebars_templates = ["handlebars", "templates"]
|
||||||
space_helmet = ["time"]
|
helmet = ["time"]
|
||||||
serve = []
|
serve = []
|
||||||
|
|
||||||
# The barage of user-facing database features.
|
# The barage of user-facing database features.
|
||||||
|
@ -73,8 +73,5 @@ time = { version = "0.1.40", optional = true }
|
||||||
[target.'cfg(debug_assertions)'.dependencies]
|
[target.'cfg(debug_assertions)'.dependencies]
|
||||||
notify = { version = "^4.0" }
|
notify = { version = "^4.0" }
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
rocket_codegen = { version = "0.4.0-dev", path = "../../core/codegen" }
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|
|
@ -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::<XssFilter>();
|
||||||
|
///
|
||||||
|
/// // 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<dyn SubPolicy>>,
|
||||||
|
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<P: Policy>(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::<NoSniff>();
|
||||||
|
/// ```
|
||||||
|
pub fn disable<P: Policy>(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::<XssFilter>());
|
||||||
|
/// assert!(helmet.is_enabled::<NoSniff>());
|
||||||
|
/// assert!(helmet.is_enabled::<Frame>());
|
||||||
|
///
|
||||||
|
/// assert!(!helmet.is_enabled::<Hsts>());
|
||||||
|
/// assert!(!helmet.is_enabled::<ExpectCt>());
|
||||||
|
/// assert!(!helmet.is_enabled::<Referrer>());
|
||||||
|
/// ```
|
||||||
|
pub fn is_enabled<P: Policy>(&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::<Hsts>()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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`] | ✗ |
|
||||||
|
//!
|
||||||
|
//! <small>? 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.</small>
|
||||||
|
//!
|
||||||
|
//! [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::<NoSniff>();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # 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::*;
|
|
@ -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<P: Policy> 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<Header<'h>> 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<Header<'static>> 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<Header<'h>> 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<Header<'static>> 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 `<frame>`,
|
||||||
|
/// [`<iframe>`][iframe] or `<object>`. This can be used to prevent
|
||||||
|
/// [clickjacking] attacks.
|
||||||
|
///
|
||||||
|
/// [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
||||||
|
/// [clickjacking]: https://en.wikipedia.org/wiki/Clickjacking
|
||||||
|
/// [owasp-clickjacking]: https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet
|
||||||
|
/// [iframe]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe
|
||||||
|
pub enum Frame {
|
||||||
|
/// Page cannot be displayed in a frame.
|
||||||
|
Deny,
|
||||||
|
|
||||||
|
/// Page can only be displayed in a frame if the page trying to render it is
|
||||||
|
/// in the same origin. Interpretation of same-origin is [browser
|
||||||
|
/// dependent][X-Frame-Options].
|
||||||
|
///
|
||||||
|
/// [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
||||||
|
SameOrigin,
|
||||||
|
|
||||||
|
/// Page can only be displayed in a frame if the page trying to render it is
|
||||||
|
/// in the origin for `Uri`. Interpretation of origin is [browser
|
||||||
|
/// dependent][X-Frame-Options].
|
||||||
|
///
|
||||||
|
/// [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
||||||
|
AllowFrom(Uri<'static>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defaults to [`Frame::SameOrigin`].
|
||||||
|
impl Default for Frame {
|
||||||
|
fn default() -> Frame {
|
||||||
|
Frame::SameOrigin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Into<Header<'static>> for &'a Frame {
|
||||||
|
fn into(self) -> Header<'static> {
|
||||||
|
let policy_string: Cow<'static, str> = match self {
|
||||||
|
Frame::Deny => "DENY".into(),
|
||||||
|
Frame::SameOrigin => "SAMEORIGIN".into(),
|
||||||
|
Frame::AllowFrom(uri) => format!("ALLOW-FROM {}", uri).into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Header::new(Frame::NAME, policy_string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [X-XSS-Protection] header: filters some forms of reflected [XSS]
|
||||||
|
/// attacks.
|
||||||
|
///
|
||||||
|
/// [X-XSS-Protection]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
|
||||||
|
/// [XSS]: https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting
|
||||||
|
pub enum XssFilter {
|
||||||
|
/// Disables XSS filtering.
|
||||||
|
Disable,
|
||||||
|
|
||||||
|
/// Enables XSS filtering. If XSS is detected, the browser will sanitize
|
||||||
|
/// before rendering the page (_SpaceHelmet default_).
|
||||||
|
Enable,
|
||||||
|
|
||||||
|
/// Enables XSS filtering. If XSS is detected, the browser will not
|
||||||
|
/// render the page.
|
||||||
|
EnableBlock,
|
||||||
|
|
||||||
|
/// Enables XSS filtering. If XSS is detected, the browser will sanitize and
|
||||||
|
/// render the page and report the violation to the given `Uri`. (_Chromium
|
||||||
|
/// only_)
|
||||||
|
EnableReport(Uri<'static>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defaults to [`XssFilter::Enable`].
|
||||||
|
impl Default for XssFilter {
|
||||||
|
fn default() -> XssFilter {
|
||||||
|
XssFilter::Enable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Into<Header<'static>> for &'a XssFilter {
|
||||||
|
fn into(self) -> Header<'static> {
|
||||||
|
let policy_string: Cow<'static, str> = match self {
|
||||||
|
XssFilter::Disable => "0".into(),
|
||||||
|
XssFilter::Enable => "1".into(),
|
||||||
|
XssFilter::EnableBlock => "1; mode=block".into(),
|
||||||
|
XssFilter::EnableReport(u) => format!("{}{}", "1; report=", u).into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Header::new(XssFilter::NAME, policy_string)
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,7 @@
|
||||||
//! * [tera_templates](templates) - Tera Templating
|
//! * [tera_templates](templates) - Tera Templating
|
||||||
//! * [uuid](uuid) - UUID (de)serialization
|
//! * [uuid](uuid) - UUID (de)serialization
|
||||||
//! * [${database}_pool](databases) - Database Configuration and Pooling
|
//! * [${database}_pool](databases) - Database Configuration and Pooling
|
||||||
//! * [space_helmet](space_helmet)
|
//! * [helmet](helmet) - Fairing for Security and Privacy Headers
|
||||||
//!
|
//!
|
||||||
//! The recommend way to include features from this crate via Cargo in your
|
//! The recommend way to include features from this crate via Cargo in your
|
||||||
//! project is by adding a `[dependencies.rocket_contrib]` section to your
|
//! project is by adding a `[dependencies.rocket_contrib]` section to your
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
#[cfg(feature="templates")] pub mod templates;
|
#[cfg(feature="templates")] pub mod templates;
|
||||||
#[cfg(feature="uuid")] pub mod uuid;
|
#[cfg(feature="uuid")] pub mod uuid;
|
||||||
#[cfg(feature="databases")] pub mod databases;
|
#[cfg(feature="databases")] pub mod databases;
|
||||||
#[cfg(feature = "space_helmet")] pub mod space_helmet;
|
#[cfg(feature = "helmet")] pub mod helmet;
|
||||||
|
|
||||||
#[cfg(feature="databases")] extern crate rocket_contrib_codegen;
|
#[cfg(feature="databases")] extern crate rocket_contrib_codegen;
|
||||||
#[cfg(feature="databases")] #[doc(hidden)] pub use rocket_contrib_codegen::*;
|
#[cfg(feature="databases")] #[doc(hidden)] pub use rocket_contrib_codegen::*;
|
||||||
|
|
|
@ -1,285 +0,0 @@
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
|
|
||||||
use rocket::fairing::{Fairing, Info, Kind};
|
|
||||||
use rocket::http::Header;
|
|
||||||
use rocket::{Request, Response, Rocket};
|
|
||||||
|
|
||||||
use super::policy::*;
|
|
||||||
|
|
||||||
/// A [`Fairing`](/rocket/fairing/trait.Fairing.html)
|
|
||||||
/// that adds HTTP headers to outgoing responses that control security features
|
|
||||||
/// on the browser.
|
|
||||||
///
|
|
||||||
/// # Usage
|
|
||||||
///
|
|
||||||
/// `SpaceHelmet` can be used in several ways.
|
|
||||||
///
|
|
||||||
/// To use it with its [defaults](#method.default), create a new instance and
|
|
||||||
/// attach it to [Rocket](/rocket/struct.Rocket.html)
|
|
||||||
/// as shown.
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate rocket;
|
|
||||||
/// # extern crate rocket_contrib;
|
|
||||||
/// use rocket_contrib::space_helmet::SpaceHelmet;
|
|
||||||
///
|
|
||||||
/// let rocket = rocket::ignite().attach(SpaceHelmet::default());
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// To enable an additional header, call the method for that header, with the
|
|
||||||
/// policy for that header, before attach e.g.
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate rocket;
|
|
||||||
/// # extern crate rocket_contrib;
|
|
||||||
/// use rocket_contrib::space_helmet::{SpaceHelmet, ReferrerPolicy};
|
|
||||||
///
|
|
||||||
/// let helmet = SpaceHelmet::default().referrer_policy(ReferrerPolicy::NoReferrer);
|
|
||||||
/// let rocket = rocket::ignite().attach(helmet);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// To disable a header, call the method for that header with `None` as
|
|
||||||
/// the policy.
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate rocket;
|
|
||||||
/// # extern crate rocket_contrib;
|
|
||||||
/// use rocket_contrib::space_helmet::SpaceHelmet;
|
|
||||||
///
|
|
||||||
/// let helmet = SpaceHelmet::default().no_sniff(None);
|
|
||||||
/// let rocket = rocket::ignite().attach(helmet);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// `SpaceHelmet` supports the builder pattern to configure multiple policies
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate rocket;
|
|
||||||
/// # extern crate rocket_contrib;
|
|
||||||
/// use rocket_contrib::space_helmet::{HstsPolicy, ExpectCtPolicy, ReferrerPolicy, SpaceHelmet};
|
|
||||||
///
|
|
||||||
/// let helmet = SpaceHelmet::default()
|
|
||||||
/// .hsts(HstsPolicy::default())
|
|
||||||
/// .expect_ct(ExpectCtPolicy::default())
|
|
||||||
/// .referrer_policy(ReferrerPolicy::default());
|
|
||||||
///
|
|
||||||
/// let rocket = rocket::ignite().attach(helmet);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # TLS and HSTS
|
|
||||||
///
|
|
||||||
/// If TLS is enabled when a [Rocket](rocket/struct.Rocket.html)
|
|
||||||
/// is [launched](/rocket/fairing/trait.Fairing.html#method.on_launch)
|
|
||||||
/// in a non-development environment e.g. [Staging](rocket/struct.Config.html#method.staging)
|
|
||||||
/// or [Production](/rocket/struct.Config.html#method.production)
|
|
||||||
/// `SpaceHelmet` enables hsts with its default policy and issue a
|
|
||||||
/// warning.
|
|
||||||
///
|
|
||||||
/// To get rid of this warning, set an [hsts](#method.hsts) policy if you are using tls.
|
|
||||||
pub struct SpaceHelmet<'a> {
|
|
||||||
expect_ct_policy: Option<ExpectCtPolicy<'a>>,
|
|
||||||
no_sniff_policy: Option<NoSniffPolicy>,
|
|
||||||
xss_protect_policy: Option<XssPolicy<'a>>,
|
|
||||||
frameguard_policy: Option<FramePolicy<'a>>,
|
|
||||||
hsts_policy: Option<HstsPolicy>,
|
|
||||||
force_hsts_policy: Option<HstsPolicy>,
|
|
||||||
force_hsts: AtomicBool,
|
|
||||||
referrer_policy: Option<ReferrerPolicy>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper for Helmet.apply
|
|
||||||
macro_rules! try_apply_header {
|
|
||||||
($self:ident, $response:ident, $policy_name:ident) => {
|
|
||||||
if let Some(ref policy) = $self.$policy_name {
|
|
||||||
if $response.set_header(policy) {
|
|
||||||
warn_!(
|
|
||||||
"(Space Helmet): set_header failed, found existing header \"{}\"",
|
|
||||||
Header::from(policy).name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Default for SpaceHelmet<'a> {
|
|
||||||
/// Returns a new `SpaceHelmet` instance. See [table](/rocket_contrib/space_helmet/index.html#what-it-supports) for
|
|
||||||
/// a description of what policies are used by default.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate rocket;
|
|
||||||
/// # extern crate rocket_contrib;
|
|
||||||
/// use rocket_contrib::space_helmet::SpaceHelmet;
|
|
||||||
///
|
|
||||||
/// let helmet = SpaceHelmet::default();
|
|
||||||
/// ```
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
expect_ct_policy: None,
|
|
||||||
no_sniff_policy: Some(NoSniffPolicy::default()),
|
|
||||||
frameguard_policy: Some(FramePolicy::default()),
|
|
||||||
xss_protect_policy: Some(XssPolicy::default()),
|
|
||||||
hsts_policy: None,
|
|
||||||
force_hsts_policy: Some(HstsPolicy::default()),
|
|
||||||
force_hsts: AtomicBool::new(false),
|
|
||||||
referrer_policy: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> SpaceHelmet<'a> {
|
|
||||||
/// Same as [`SpaceHelmet::default()`].
|
|
||||||
pub fn new() -> Self {
|
|
||||||
SpaceHelmet::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the [X-XSS-Protection](
|
|
||||||
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection)
|
|
||||||
/// header to the given `policy` or disables it if `policy == None`.
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate rocket;
|
|
||||||
/// # extern crate rocket_contrib;
|
|
||||||
/// use rocket::http::uri::Uri;
|
|
||||||
/// use rocket_contrib::space_helmet::{SpaceHelmet, XssPolicy};
|
|
||||||
///
|
|
||||||
/// let report_uri = Uri::parse("https://www.google.com").unwrap();
|
|
||||||
/// let helmet = SpaceHelmet::new().xss_protect(XssPolicy::EnableReport(report_uri));
|
|
||||||
/// ```
|
|
||||||
pub fn xss_protect<T: Into<Option<XssPolicy<'a>>>>(mut self, policy: T) -> SpaceHelmet<'a> {
|
|
||||||
self.xss_protect_policy = policy.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the [X-Content-Type-Options](
|
|
||||||
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options)
|
|
||||||
/// header to `policy` or disables it if `policy == None`.
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
///
|
|
||||||
/// use rocket_contrib::space_helmet::{SpaceHelmet, NoSniffPolicy};
|
|
||||||
///
|
|
||||||
/// let helmet = SpaceHelmet::new().no_sniff(NoSniffPolicy::Enable);
|
|
||||||
/// ```
|
|
||||||
pub fn no_sniff<T: Into<Option<NoSniffPolicy>>>(mut self, policy: T) -> SpaceHelmet<'a> {
|
|
||||||
self.no_sniff_policy = policy.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the [X-Frame-Options](
|
|
||||||
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options)
|
|
||||||
/// header to `policy`, or disables it if `policy == None`.
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate rocket;
|
|
||||||
/// # extern crate rocket_contrib;
|
|
||||||
///
|
|
||||||
/// use rocket::http::uri::Uri;
|
|
||||||
/// use rocket_contrib::space_helmet::{SpaceHelmet, FramePolicy};
|
|
||||||
///
|
|
||||||
/// let allow_uri = Uri::parse("https://www.google.com").unwrap();
|
|
||||||
/// let helmet = SpaceHelmet::new().frameguard(FramePolicy::AllowFrom(allow_uri));
|
|
||||||
/// ```
|
|
||||||
pub fn frameguard<T: Into<Option<FramePolicy<'a>>>>(mut self, policy: T) -> SpaceHelmet<'a> {
|
|
||||||
self.frameguard_policy = policy.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the [Strict-Transport-Security](
|
|
||||||
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security)
|
|
||||||
/// header to `policy`, or disables it if `policy == None`.
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use rocket_contrib::space_helmet::{SpaceHelmet, HstsPolicy};
|
|
||||||
///
|
|
||||||
/// let helmet = SpaceHelmet::new().hsts(HstsPolicy::default());
|
|
||||||
/// ```
|
|
||||||
pub fn hsts<T: Into<Option<HstsPolicy>>>(mut self, policy: T) -> SpaceHelmet<'a> {
|
|
||||||
self.hsts_policy = policy.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the [Expect-CT](
|
|
||||||
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT)
|
|
||||||
/// header to `policy`, or disables it if `policy == None`.
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate rocket;
|
|
||||||
/// # extern crate rocket_contrib;
|
|
||||||
/// # extern crate time;
|
|
||||||
/// use rocket::http::uri::Uri;
|
|
||||||
/// use rocket_contrib::space_helmet::{SpaceHelmet, ExpectCtPolicy};
|
|
||||||
/// use time::Duration;
|
|
||||||
///
|
|
||||||
/// let report_uri = Uri::parse("https://www.google.com").unwrap();
|
|
||||||
/// let helmet = SpaceHelmet::new()
|
|
||||||
/// .expect_ct(ExpectCtPolicy::ReportAndEnforce(
|
|
||||||
/// Duration::days(30), report_uri));
|
|
||||||
/// ```
|
|
||||||
pub fn expect_ct<T: Into<Option<ExpectCtPolicy<'a>>>>(mut self, policy: T) -> SpaceHelmet<'a> {
|
|
||||||
self.expect_ct_policy = policy.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the [Referrer-Policy](
|
|
||||||
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy)
|
|
||||||
/// header to `policy`, or disables it if `policy == None`.
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate rocket;
|
|
||||||
/// # extern crate rocket_contrib;
|
|
||||||
///
|
|
||||||
/// use rocket_contrib::space_helmet::{ReferrerPolicy, SpaceHelmet};
|
|
||||||
///
|
|
||||||
/// let helmet = SpaceHelmet::new().referrer_policy(ReferrerPolicy::NoReferrer);
|
|
||||||
/// ```
|
|
||||||
pub fn referrer_policy<T: Into<Option<ReferrerPolicy>>>(
|
|
||||||
mut self,
|
|
||||||
policy: T,
|
|
||||||
) -> SpaceHelmet<'a> {
|
|
||||||
self.referrer_policy = policy.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply(&self, response: &mut Response) {
|
|
||||||
try_apply_header!(self, response, no_sniff_policy);
|
|
||||||
try_apply_header!(self, response, xss_protect_policy);
|
|
||||||
try_apply_header!(self, response, frameguard_policy);
|
|
||||||
try_apply_header!(self, response, expect_ct_policy);
|
|
||||||
try_apply_header!(self, response, referrer_policy);
|
|
||||||
if self.hsts_policy.is_some() {
|
|
||||||
try_apply_header!(self, response, hsts_policy);
|
|
||||||
} else {
|
|
||||||
if self.force_hsts.load(Ordering::Relaxed) {
|
|
||||||
try_apply_header!(self, response, force_hsts_policy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fairing for SpaceHelmet<'static> {
|
|
||||||
fn info(&self) -> Info {
|
|
||||||
Info {
|
|
||||||
name: "Rocket SpaceHelmet (HTTP Security Headers)",
|
|
||||||
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.hsts_policy.is_some()
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
//! [`SpaceHelmet`] is a [`Fairing`](/rocket/fairing/trait.Fairing.html) that
|
|
||||||
//! turns on browsers security features by adding HTTP headers to all outgoing
|
|
||||||
//! responses.
|
|
||||||
//!
|
|
||||||
//! It provides a typed interface for http security headers and takes some
|
|
||||||
//! inspiration from [helmet](https://helmetjs.github.io/), a similar piece
|
|
||||||
//! of middleware for [express](https://expressjs.com).
|
|
||||||
//!
|
|
||||||
//! ### What it supports
|
|
||||||
//!
|
|
||||||
//! | HTTP Header | Description | Method | Enabled by Default? |
|
|
||||||
//! | --------------------------- | -------------------------------------- | ---------------------------------- | ------------------- |
|
|
||||||
//! | [X-XSS-Protection] | Prevents some reflected XSS attacks. | [`SpaceHelmet::xss_protect()`] | ✔ |
|
|
||||||
//! | [X-Content-Type-Options] | Prevents client sniffing of MIME type. | [`SpaceHelmet::no_sniff()`] | ✔ |
|
|
||||||
//! | [X-Frame-Options] | Prevents [clickjacking]. | [`SpaceHelmet::frameguard()`] | ✔ |
|
|
||||||
//! | [Strict-Transport-Security] | Enforces strict use of HTTPS. | [`SpaceHelmet::hsts()`] | ? |
|
|
||||||
//! | [Expect-CT] | Enables certificate transparency. | [`SpaceHelmet::expect_ct()`] | ✗ |
|
|
||||||
//! | [Referrer-Policy] | Enables referrer policy. | [`SpaceHelmet::referrer_policy()`] | ✗ |
|
|
||||||
//!
|
|
||||||
//! [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
|
|
||||||
//! _? If tls is enabled when a [Rocket](/rocket/struct.Rocket.html) is
|
|
||||||
//! [launched()'ed](/rocket/fairing/trait.Fairing.html)
|
|
||||||
//! in a non-development environment e.g. [Staging](/rocket/struct.Config.html#method.staging)
|
|
||||||
//! or [Production](/rocket/struct.Config.html#method.production), `SpaceHelmet` enables hsts with its
|
|
||||||
//! default policy and outputs a warning._
|
|
||||||
//!
|
|
||||||
//! ### Examples
|
|
||||||
//!
|
|
||||||
//! To apply the headers that are enabled by default, just create a new [`SpaceHelmet`] instance
|
|
||||||
//! and attach before launch.
|
|
||||||
//!
|
|
||||||
//! ```rust
|
|
||||||
//! # extern crate rocket;
|
|
||||||
//! # extern crate rocket_contrib;
|
|
||||||
//! use rocket_contrib::space_helmet::{SpaceHelmet};
|
|
||||||
//!
|
|
||||||
//! let rocket = rocket::ignite().attach(SpaceHelmet::new());
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Each header can be configured individually if desired. To enable a particular
|
|
||||||
//! header, call the method for the header on the [`SpaceHelmet`] instance. Multiple
|
|
||||||
//! method calls can be chained in a builder pattern as illustrated below.
|
|
||||||
//!
|
|
||||||
//! ```rust
|
|
||||||
//! # extern crate rocket;
|
|
||||||
//! # extern crate rocket_contrib;
|
|
||||||
//! use rocket::http::uri::Uri;
|
|
||||||
//!
|
|
||||||
//! // Every header has a corresponding policy type.
|
|
||||||
//! use rocket_contrib::space_helmet::{SpaceHelmet, FramePolicy, XssPolicy, HstsPolicy};
|
|
||||||
//!
|
|
||||||
//! let site_uri = Uri::parse("https://mysite.example.com").unwrap();
|
|
||||||
//! let report_uri = Uri::parse("https://report.example.com").unwrap();
|
|
||||||
//! let helmet = SpaceHelmet::new()
|
|
||||||
//! .hsts(HstsPolicy::default()) // each policy has a default.
|
|
||||||
//! .no_sniff(None) // setting policy to None disables the header.
|
|
||||||
//! .frameguard(FramePolicy::AllowFrom(site_uri))
|
|
||||||
//! .xss_protect(XssPolicy::EnableReport(report_uri));
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! #### Still have questions?
|
|
||||||
//!
|
|
||||||
//! * _What policy should I choose?_ Check out the links in the table
|
|
||||||
//! above for individual header documentation. The [helmetjs](https://helmetjs.github.io/) doc's
|
|
||||||
//! are also a good resource, and owasp has a collection of [references] on these headers.
|
|
||||||
//!
|
|
||||||
//! [references]: https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#tab=Headers
|
|
||||||
//!
|
|
||||||
//! * _Where I can I read more about using fairings?_ Check out the
|
|
||||||
//! [fairing](https://rocket.rs/guide/fairings/) section of the rocket guide.
|
|
||||||
//!
|
|
||||||
//! * _Do I only need those headers `SpaceHelmet` enables by default?_ Maybe, the other headers
|
|
||||||
//! can protect against many important vulnerabilities. Please consult their documentation and
|
|
||||||
//! other resources to find out if they are needed for your project.
|
|
||||||
|
|
||||||
extern crate time;
|
|
||||||
|
|
||||||
mod helmet;
|
|
||||||
mod policy;
|
|
||||||
|
|
||||||
pub use self::helmet::*;
|
|
||||||
pub use self::policy::*;
|
|
|
@ -1,302 +0,0 @@
|
||||||
use std::borrow::Cow;
|
|
||||||
use rocket::http::uri::Uri;
|
|
||||||
use rocket::http::Header;
|
|
||||||
|
|
||||||
use super::time::Duration;
|
|
||||||
|
|
||||||
/// The [Referrer-Policy] header tells the browser if 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 ReferrerPolicy {
|
|
||||||
/// 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 [`ReferrerPolicy::NoReferrer`]. Tells the browser Omit the `Referer` header.
|
|
||||||
impl Default for ReferrerPolicy {
|
|
||||||
fn default() -> ReferrerPolicy {
|
|
||||||
ReferrerPolicy::NoReferrer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> From<&'a ReferrerPolicy> for Header<'b> {
|
|
||||||
fn from(policy: &ReferrerPolicy) -> Header<'b> {
|
|
||||||
let policy_string = match policy {
|
|
||||||
ReferrerPolicy::NoReferrer => "no-referrer",
|
|
||||||
ReferrerPolicy::NoReferrerWhenDowngrade => "no-referrer-when-downgrade",
|
|
||||||
ReferrerPolicy::Origin => "origin",
|
|
||||||
ReferrerPolicy::OriginWhenCrossOrigin => "origin-when-cross-origin",
|
|
||||||
ReferrerPolicy::SameOrigin => "same-origin",
|
|
||||||
ReferrerPolicy::StrictOrigin => "strict-origin",
|
|
||||||
ReferrerPolicy::StrictOriginWhenCrossOrigin => {
|
|
||||||
"strict-origin-when-cross-origin"
|
|
||||||
}
|
|
||||||
ReferrerPolicy::UnsafeUrl => "unsafe-url",
|
|
||||||
};
|
|
||||||
|
|
||||||
Header::new("Referrer-Policy", policy_string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// The [Expect-CT] header tells browser to enable [Certificate Transparency] checking, which
|
|
||||||
/// can detect and prevent the misuse of the site's certificate. Read all [`ExpectCtPolicy`]
|
|
||||||
/// documentation before use.
|
|
||||||
///
|
|
||||||
/// [Certificate Transparency]
|
|
||||||
/// solves a variety of problems with public TLS/SSL certificate management and is valuable
|
|
||||||
/// measure if your standing up a public website. If your [getting started] with certificate
|
|
||||||
/// transparency, be sure that your [site is in compliance][getting started] before you turn on
|
|
||||||
/// enforcement with [`ExpectCtPolicy::Enforce`] or [`ExpectCtPolicy::ReportAndEnforce`]
|
|
||||||
/// otherwise the browser will stop talking to your site until you bring it into compliance
|
|
||||||
/// or [`Duration`] time elapses. _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 ExpectCtPolicy<'a> {
|
|
||||||
/// Tells browser to enforce certificate compliance for [`Duration`] seconds.
|
|
||||||
/// Check if your site is in compliance before turning on enforcement.
|
|
||||||
/// (_SpaceHelmet_ default).
|
|
||||||
Enforce(Duration),
|
|
||||||
|
|
||||||
/// Tells browser to report compliance violations certificate transparency for [`Duration`]
|
|
||||||
/// seconds. 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<'a>),
|
|
||||||
|
|
||||||
/// Enforces compliance and supports notification to if there has been a violation for
|
|
||||||
/// [`Duration`].
|
|
||||||
ReportAndEnforce(Duration, Uri<'a>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defaults to [`ExpectCtPolicy::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<'a> Default for ExpectCtPolicy<'a> {
|
|
||||||
fn default() -> ExpectCtPolicy<'a> {
|
|
||||||
ExpectCtPolicy::Enforce(Duration::days(30))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> From<&'a ExpectCtPolicy<'a>> for Header<'b> {
|
|
||||||
fn from(policy: &ExpectCtPolicy<'a>) -> Header<'b> {
|
|
||||||
let policy_string = match policy {
|
|
||||||
ExpectCtPolicy::Enforce(max_age) => format!("max-age={}, enforce", max_age.num_seconds()),
|
|
||||||
ExpectCtPolicy::Report(max_age, url) => {
|
|
||||||
format!("max-age={}, report-uri=\"{}\"", max_age.num_seconds(), url)
|
|
||||||
}
|
|
||||||
ExpectCtPolicy::ReportAndEnforce(max_age, url) => {
|
|
||||||
format!("max-age={}, enforce, report-uri=\"{}\"", max_age.num_seconds(), url)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Header::new("Expect-CT", policy_string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The [X-Content-Type-Options] header can tell the browser to turn off [mime sniffing](
|
|
||||||
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#MIME_sniffing) which
|
|
||||||
/// can prevent certain [attacks].
|
|
||||||
///
|
|
||||||
/// [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 NoSniffPolicy {
|
|
||||||
|
|
||||||
///Turns off mime sniffing.
|
|
||||||
Enable,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defaults to [`NoSniffPolicy::Enable`], turns off mime sniffing.
|
|
||||||
impl Default for NoSniffPolicy {
|
|
||||||
fn default() -> NoSniffPolicy {
|
|
||||||
NoSniffPolicy::Enable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> From<&'a NoSniffPolicy> for Header<'b> {
|
|
||||||
fn from(_policy: &NoSniffPolicy) -> Header<'b> {
|
|
||||||
Header::new("X-Content-Type-Options", "nosniff")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The HTTP [Strict-Transport-Security] (HSTS) header 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 in [Staging ] or [Production].
|
|
||||||
/// Read full [`HstsPolicy`] documentation before you configure this.
|
|
||||||
///
|
|
||||||
/// HSTS is important for HTTPS security, however, 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, just roll it out with care.
|
|
||||||
///
|
|
||||||
/// Finally, requiring TLS use with valid certificates may be something of a nuisance in
|
|
||||||
/// development settings, so you may with to restrict HSTS use to [Staging] and [Production].
|
|
||||||
///
|
|
||||||
/// [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/space_helmet/enum.HstsPolicy.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 HstsPolicy {
|
|
||||||
/// Browser should only permit this site to be accesses by HTTPS for the next [`Duration`]
|
|
||||||
/// seconds.
|
|
||||||
Enable(Duration),
|
|
||||||
|
|
||||||
/// Same as above, but also apply to all of the sites 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 `HstsPolicy::Enable(Duration::weeks(52))`.
|
|
||||||
impl Default for HstsPolicy {
|
|
||||||
fn default() -> HstsPolicy {
|
|
||||||
HstsPolicy::Enable(Duration::weeks(52))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> From<&'a HstsPolicy> for Header<'b> {
|
|
||||||
fn from(policy: &HstsPolicy) -> Header<'b> {
|
|
||||||
let policy_string = match policy {
|
|
||||||
HstsPolicy::Enable(max_age) => format!("max-age={}", max_age.num_seconds()),
|
|
||||||
HstsPolicy::IncludeSubDomains(max_age) => {
|
|
||||||
format!("max-age={}; includeSubDomains", max_age.num_seconds())
|
|
||||||
}
|
|
||||||
HstsPolicy::Preload(max_age) => format!("max-age={}; preload", max_age.num_seconds()),
|
|
||||||
};
|
|
||||||
|
|
||||||
Header::new("Strict-Transport-Security", policy_string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The [X-Frame-Options] header controls whether the browser should allow the page
|
|
||||||
/// to render in a `<frame>`, [`<iframe>`][iframe] or `<object>`. This can be used
|
|
||||||
/// to prevent [clickjacking] attacks.
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
|
||||||
/// [clickjacking]: https://en.wikipedia.org/wiki/Clickjacking
|
|
||||||
/// [owasp-clickjacking]: https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet
|
|
||||||
/// [iframe]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe
|
|
||||||
pub enum FramePolicy<'a> {
|
|
||||||
/// Page cannot be displayed in a frame.
|
|
||||||
Deny,
|
|
||||||
|
|
||||||
/// Page can only be displayed in a frame if the page trying to render it came from
|
|
||||||
/// the same-origin. Interpretation of same-origin is [browser dependant][X-Frame-Options].
|
|
||||||
///
|
|
||||||
/// [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
|
||||||
SameOrigin,
|
|
||||||
|
|
||||||
/// Page can only be displayed in a frame if the page trying to render it came from
|
|
||||||
/// the origin given `Uri`. Interpretation of origin is [browser dependant][X-Frame-Options].
|
|
||||||
///
|
|
||||||
/// [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
|
||||||
AllowFrom(Uri<'a>),
|
|
||||||
}
|
|
||||||
|
|
||||||
///Defaults to [`FramePolicy::SameOrigin`].
|
|
||||||
impl<'a> Default for FramePolicy<'a> {
|
|
||||||
fn default() -> FramePolicy<'a> {
|
|
||||||
FramePolicy::SameOrigin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> From<&'a FramePolicy<'a>> for Header<'b> {
|
|
||||||
fn from(policy: &FramePolicy<'a>) -> Header<'b> {
|
|
||||||
let policy_string: Cow<'static, str> = match policy {
|
|
||||||
FramePolicy::Deny => "DENY".into(),
|
|
||||||
FramePolicy::SameOrigin => "SAMEORIGIN".into(),
|
|
||||||
FramePolicy::AllowFrom(uri) => format!("ALLOW-FROM {}", uri).into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Header::new("X-Frame-Options", policy_string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The [X-XSS-Protection] header tells the browsers to filter some forms of reflected
|
|
||||||
/// XSS([cross-site scripting]) attacks.
|
|
||||||
///
|
|
||||||
/// [X-XSS-Protection]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
|
|
||||||
/// [cross-site scripting]: https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting
|
|
||||||
pub enum XssPolicy<'a> {
|
|
||||||
/// Disables XSS filtering.
|
|
||||||
Disable,
|
|
||||||
|
|
||||||
/// Enables XSS filtering, if XSS detected browser will sanitize and render page (_often browser
|
|
||||||
/// default_).
|
|
||||||
Enable,
|
|
||||||
|
|
||||||
/// Enables XSS filtering, if XSS detected browser will block rendering of page (_SpaceHelmet default_).
|
|
||||||
EnableBlock,
|
|
||||||
|
|
||||||
/// Enables XSS filtering, if XSS detected browser will sanitize and render page and report the
|
|
||||||
/// violation to the given `Uri`. (Chromium only)
|
|
||||||
EnableReport(Uri<'a>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defaults to [`XssPolicy::EnableBlock`], turns on XSS filtering and blocks page rendering if
|
|
||||||
/// detected.
|
|
||||||
impl<'a> Default for XssPolicy<'a> {
|
|
||||||
fn default() -> XssPolicy<'a> {
|
|
||||||
XssPolicy::EnableBlock
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> From<&'a XssPolicy<'a>> for Header<'b> {
|
|
||||||
fn from(policy: &XssPolicy) -> Header<'b> {
|
|
||||||
let policy_string: Cow<'static, str> = match policy {
|
|
||||||
XssPolicy::Disable => "0".into(),
|
|
||||||
XssPolicy::Enable => "1".into(),
|
|
||||||
XssPolicy::EnableBlock => "1; mode=block".into(),
|
|
||||||
XssPolicy::EnableReport(u) => format!("{}{}", "1; report=", u).into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Header::new("X-XSS-Protection", policy_string)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
#![feature(proc_macro_hygiene, decl_macro)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
#[cfg(feature = "helmet")]
|
||||||
|
extern crate rocket;
|
||||||
|
|
||||||
|
#[cfg(feature = "helmet")]
|
||||||
|
mod helmet_tests {
|
||||||
|
extern crate time;
|
||||||
|
extern crate rocket_contrib;
|
||||||
|
|
||||||
|
use rocket;
|
||||||
|
use rocket::http::{Status, uri::Uri};
|
||||||
|
use rocket::local::{Client, LocalResponse};
|
||||||
|
|
||||||
|
use self::rocket_contrib::helmet::*;
|
||||||
|
use self::rocket_contrib::helmet::*;
|
||||||
|
use self::time::Duration;
|
||||||
|
|
||||||
|
#[get("/")] fn hello() { }
|
||||||
|
|
||||||
|
macro_rules! assert_header {
|
||||||
|
($response:ident, $name:expr, $value:expr) => {
|
||||||
|
match $response.headers().get_one($name) {
|
||||||
|
Some(value) => assert_eq!(value, $value),
|
||||||
|
None => panic!("missing header '{}' with value '{}'", $name, $value)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! assert_no_header {
|
||||||
|
($response:ident, $name:expr) => {
|
||||||
|
if let Some(value) = $response.headers().get_one($name) {
|
||||||
|
panic!("unexpected header: '{}={}", $name, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! dispatch {
|
||||||
|
($helmet:expr, $closure:expr) => {{
|
||||||
|
let rocket = rocket::ignite().mount("/", routes![hello]).attach($helmet);
|
||||||
|
let client = Client::new(rocket).unwrap();
|
||||||
|
let response = client.get("/").dispatch();
|
||||||
|
assert_eq!(response.status(), Status::Ok);
|
||||||
|
$closure(response)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn default_headers_test() {
|
||||||
|
dispatch!(SpaceHelmet::default(), |response: LocalResponse| {
|
||||||
|
assert_header!(response, "X-XSS-Protection", "1");
|
||||||
|
assert_header!(response, "X-Frame-Options", "SAMEORIGIN");
|
||||||
|
assert_header!(response, "X-Content-Type-Options", "nosniff");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn disable_headers_test() {
|
||||||
|
let helmet = SpaceHelmet::default().disable::<XssFilter>();
|
||||||
|
dispatch!(helmet, |response: LocalResponse| {
|
||||||
|
assert_header!(response, "X-Frame-Options", "SAMEORIGIN");
|
||||||
|
assert_header!(response, "X-Content-Type-Options", "nosniff");
|
||||||
|
assert_no_header!(response, "X-XSS-Protection");
|
||||||
|
});
|
||||||
|
|
||||||
|
let helmet = SpaceHelmet::default().disable::<Frame>();
|
||||||
|
dispatch!(helmet, |response: LocalResponse| {
|
||||||
|
assert_header!(response, "X-XSS-Protection", "1");
|
||||||
|
assert_header!(response, "X-Content-Type-Options", "nosniff");
|
||||||
|
assert_no_header!(response, "X-Frame-Options");
|
||||||
|
});
|
||||||
|
|
||||||
|
let helmet = SpaceHelmet::default()
|
||||||
|
.disable::<Frame>()
|
||||||
|
.disable::<XssFilter>()
|
||||||
|
.disable::<NoSniff>();
|
||||||
|
|
||||||
|
dispatch!(helmet, |response: LocalResponse| {
|
||||||
|
assert_no_header!(response, "X-Frame-Options");
|
||||||
|
assert_no_header!(response, "X-XSS-Protection");
|
||||||
|
assert_no_header!(response, "X-Content-Type-Options");
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch!(SpaceHelmet::new(), |response: LocalResponse| {
|
||||||
|
assert_no_header!(response, "X-Frame-Options");
|
||||||
|
assert_no_header!(response, "X-XSS-Protection");
|
||||||
|
assert_no_header!(response, "X-Content-Type-Options");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn additional_headers_test() {
|
||||||
|
let helmet = SpaceHelmet::default()
|
||||||
|
.enable(Hsts::default())
|
||||||
|
.enable(ExpectCt::default())
|
||||||
|
.enable(Referrer::default());
|
||||||
|
|
||||||
|
dispatch!(helmet, |response: LocalResponse| {
|
||||||
|
assert_header!(
|
||||||
|
response,
|
||||||
|
"Strict-Transport-Security",
|
||||||
|
format!("max-age={}", Duration::weeks(52).num_seconds())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_header!(
|
||||||
|
response,
|
||||||
|
"Expect-CT",
|
||||||
|
format!("max-age={}, enforce", Duration::days(30).num_seconds())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_header!(response, "Referrer-Policy", "no-referrer");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn uri_test() {
|
||||||
|
let allow_uri = Uri::parse("https://www.google.com").unwrap();
|
||||||
|
let report_uri = Uri::parse("https://www.google.com").unwrap();
|
||||||
|
let enforce_uri = Uri::parse("https://www.google.com").unwrap();
|
||||||
|
|
||||||
|
let helmet = SpaceHelmet::default()
|
||||||
|
.enable(Frame::AllowFrom(allow_uri))
|
||||||
|
.enable(XssFilter::EnableReport(report_uri))
|
||||||
|
.enable(ExpectCt::ReportAndEnforce(Duration::seconds(30), enforce_uri));
|
||||||
|
|
||||||
|
dispatch!(helmet, |response: LocalResponse| {
|
||||||
|
assert_header!(response, "X-Frame-Options",
|
||||||
|
"ALLOW-FROM https://www.google.com");
|
||||||
|
|
||||||
|
assert_header!(response, "X-XSS-Protection",
|
||||||
|
"1; report=https://www.google.com");
|
||||||
|
|
||||||
|
assert_header!(response, "Expect-CT",
|
||||||
|
"max-age=30, enforce, report-uri=\"https://www.google.com\"");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,99 +0,0 @@
|
||||||
#![cfg_attr(test, feature(plugin, decl_macro))]
|
|
||||||
#![cfg_attr(test, plugin(rocket_codegen))]
|
|
||||||
#![feature(proc_macro_non_items)]
|
|
||||||
|
|
||||||
#[macro_use] extern crate rocket;
|
|
||||||
extern crate rocket_contrib;
|
|
||||||
|
|
||||||
#[cfg(feature = "space_helmet")]
|
|
||||||
extern crate time;
|
|
||||||
|
|
||||||
#[cfg(feature = "space_helmet")]
|
|
||||||
mod space_helmet_tests {
|
|
||||||
use rocket;
|
|
||||||
use rocket::http::uri::Uri;
|
|
||||||
use rocket::http::Status;
|
|
||||||
use rocket::local::Client;
|
|
||||||
use rocket_contrib::space_helmet::*;
|
|
||||||
use time::Duration;
|
|
||||||
|
|
||||||
#[get("/")]
|
|
||||||
fn hello() -> &'static str {
|
|
||||||
"Hello, world!"
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! check_header {
|
|
||||||
($response:ident, $header_name:expr, $header_param:expr) => {
|
|
||||||
match $response.headers().get_one($header_name) {
|
|
||||||
Some(string) => assert_eq!(string, $header_param),
|
|
||||||
None => panic!("missing header parameters")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn default_headers_test() {
|
|
||||||
let helmet = SpaceHelmet::new();
|
|
||||||
let rocket = rocket::ignite().mount("/", routes![hello]).attach(helmet);
|
|
||||||
let client = Client::new(rocket).unwrap();
|
|
||||||
let mut response = client.get("/").dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.body_string(), Some("Hello, world!".into()));
|
|
||||||
check_header!(response, "X-XSS-Protection", "1; mode=block");
|
|
||||||
check_header!(response, "X-Frame-Options", "SAMEORIGIN");
|
|
||||||
check_header!(response, "X-Content-Type-Options", "nosniff");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn additional_headers_test() {
|
|
||||||
let helmet = SpaceHelmet::new()
|
|
||||||
.hsts(HstsPolicy::default())
|
|
||||||
.expect_ct(ExpectCtPolicy::default())
|
|
||||||
.referrer_policy(ReferrerPolicy::default());
|
|
||||||
let rocket = rocket::ignite().mount("/", routes![hello]).attach(helmet);
|
|
||||||
let client = Client::new(rocket).unwrap();
|
|
||||||
let mut response = client.get("/").dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.body_string(), Some("Hello, world!".into()));
|
|
||||||
check_header!(
|
|
||||||
response,
|
|
||||||
"Strict-Transport-Security",
|
|
||||||
format!("max-age={}", Duration::weeks(52).num_seconds())
|
|
||||||
);
|
|
||||||
check_header!(
|
|
||||||
response,
|
|
||||||
"Expect-CT",
|
|
||||||
format!("max-age={}, enforce", Duration::days(30).num_seconds())
|
|
||||||
);
|
|
||||||
check_header!(response, "Referrer-Policy", "no-referrer");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn uri_test() {
|
|
||||||
let allow_uri = Uri::parse("https://www.google.com").unwrap();
|
|
||||||
let report_uri = Uri::parse("https://www.google.com").unwrap();
|
|
||||||
let enforce_uri = Uri::parse("https://www.google.com").unwrap();
|
|
||||||
let helmet = SpaceHelmet::new()
|
|
||||||
.frameguard(FramePolicy::AllowFrom(allow_uri))
|
|
||||||
.xss_protect(XssPolicy::EnableReport(report_uri))
|
|
||||||
.expect_ct(ExpectCtPolicy::ReportAndEnforce(Duration::seconds(30), enforce_uri));
|
|
||||||
let rocket = rocket::ignite().mount("/", routes![hello]).attach(helmet);
|
|
||||||
let client = Client::new(rocket).unwrap();
|
|
||||||
let response = client.get("/").dispatch();
|
|
||||||
check_header!(
|
|
||||||
response,
|
|
||||||
"X-Frame-Options",
|
|
||||||
"ALLOW-FROM https://www.google.com"
|
|
||||||
);
|
|
||||||
check_header!(
|
|
||||||
response,
|
|
||||||
"X-XSS-Protection",
|
|
||||||
"1; report=https://www.google.com"
|
|
||||||
);
|
|
||||||
check_header!(
|
|
||||||
response,
|
|
||||||
"Expect-CT",
|
|
||||||
"max-age=30, enforce, report-uri=\"https://www.google.com\""
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "space_helmet"
|
|
||||||
version = "0.0.0"
|
|
||||||
workspace = "../../"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
rocket = { path = "../../core/lib", features = ["tls"]}
|
|
||||||
rocket_codegen = { path = "../../core/codegen" }
|
|
||||||
|
|
||||||
[dependencies.rocket_contrib]
|
|
||||||
path = "../../contrib/lib"
|
|
||||||
default-features = false
|
|
||||||
features = ["space_helmet"]
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "hello"
|
|
||||||
path = "src/hello.rs"
|
|
|
@ -1,4 +0,0 @@
|
||||||
# Example cert and key pair from the tls example, NEVER use these in production, for demonstration only!!!
|
|
||||||
[global.tls]
|
|
||||||
certs = "private/cert.pem"
|
|
||||||
key = "private/key.pem"
|
|
|
@ -1,30 +0,0 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
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
|
|
||||||
8wg+/u6aK2bMHt41f3XE/UTY+A57
|
|
||||||
-----END CERTIFICATE-----
|
|
|
@ -1,51 +0,0 @@
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
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
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
|
@ -1,33 +0,0 @@
|
||||||
#![feature(decl_macro)]
|
|
||||||
#![feature(plugin)]
|
|
||||||
#![plugin(rocket_codegen)]
|
|
||||||
#![feature(proc_macro_non_items)]
|
|
||||||
|
|
||||||
#[macro_use] extern crate rocket;
|
|
||||||
extern crate rocket_contrib;
|
|
||||||
use rocket::http::uri::Uri;
|
|
||||||
|
|
||||||
use rocket_contrib::space_helmet::*;
|
|
||||||
|
|
||||||
#[get("/")]
|
|
||||||
fn index() -> &'static str {
|
|
||||||
"Hello, world!"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rocket() -> rocket::Rocket {
|
|
||||||
let allow_uri = Uri::parse("https://mysite.example.com").unwrap();
|
|
||||||
let report_uri = Uri::parse("https://report.example.com").unwrap();
|
|
||||||
let helmet = SpaceHelmet::new()
|
|
||||||
//illustrates how to disable a header by using None as the policy.
|
|
||||||
.no_sniff(None)
|
|
||||||
.frameguard(FramePolicy::AllowFrom(allow_uri))
|
|
||||||
.xss_protect(XssPolicy::EnableReport(report_uri))
|
|
||||||
//we need an hsts policy as we are using tls
|
|
||||||
.hsts(HstsPolicy::default())
|
|
||||||
.expect_ct(ExpectCtPolicy::default());
|
|
||||||
rocket::ignite().mount("/", routes![index]).attach(helmet)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
rocket().launch();
|
|
||||||
}
|
|
|
@ -86,7 +86,7 @@ if [ "$1" = "--contrib" ]; then
|
||||||
tera_templates
|
tera_templates
|
||||||
handlebars_templates
|
handlebars_templates
|
||||||
serve
|
serve
|
||||||
space_helmet
|
helmet
|
||||||
diesel_postgres_pool
|
diesel_postgres_pool
|
||||||
diesel_sqlite_pool
|
diesel_sqlite_pool
|
||||||
diesel_mysql_pool
|
diesel_mysql_pool
|
||||||
|
|
Loading…
Reference in New Issue