Skip to content

Commit

Permalink
feat: add HSTS header
Browse files Browse the repository at this point in the history
closes #6
  • Loading branch information
its-danny committed Aug 20, 2024
1 parent b7733f3 commit 36518c4
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 3 deletions.
5 changes: 4 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use http::StatusCode;

#[derive(Debug, thiserror::Error, PartialEq)]
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Maps the [`hmac::digest::InvalidLength`] error.
#[error(transparent)]
InvalidLength(#[from] hmac::digest::InvalidLength),
/// Maps the [`http::header::InvalidHeaderValue`] error.
#[error(transparent)]
InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),
/// An expected extension was missing.
#[error("couldn't extract `{0}`. is `SurfLayer` enabled?")]
ExtensionNotFound(String),
Expand Down
53 changes: 51 additions & 2 deletions src/surf.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use futures_util::future::BoxFuture;
use http::{Request, Response};
use http::{HeaderValue, Request, Response};
use secstr::SecStr;
use std::{
sync::Arc,
Expand All @@ -20,8 +20,10 @@ pub(crate) struct Config {
pub(crate) cookie_name: String,
pub(crate) expires: Expiration,
pub(crate) header_name: String,
pub(crate) hsts: bool,
pub(crate) http_only: bool,
pub(crate) prefix: bool,
pub(crate) preload: bool,
pub(crate) same_site: SameSite,
pub(crate) secure: bool,
}
Expand Down Expand Up @@ -55,8 +57,10 @@ impl Surf {
cookie_name: "csrf_token".into(),
expires: Expiration::Session,
header_name: "X-CSRF-Token".into(),
hsts: true,
http_only: true,
prefix: true,
preload: false,
same_site: SameSite::Strict,
secure: true,
},
Expand Down Expand Up @@ -86,6 +90,15 @@ impl Surf {
self
}

/// Sets whether to send the `Strict-Transport-Security` header.
///
/// See: [HTTP Strict Transport Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Strict_Transport_Security_Cheat_Sheet.html)
pub fn hsts(mut self, hsts: bool) -> Self {
self.config.hsts = hsts;

self
}

/// Sets the `HTTPOnly` attribute of the cookie. The default value is `true`.
///
/// ⚠️ **Warning**: This should generally _not_ be set to false.
Expand All @@ -106,6 +119,15 @@ impl Surf {
self
}

/// Sets whether to append the [hsts](`Surf::hsts`) header with `preload`.
///
/// See: [HTTP Strict Transport Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Strict_Transport_Security_Cheat_Sheet.html)
pub fn preload(mut self, preload: bool) -> Self {
self.config.preload = preload;

self
}

/// Sets the `SameSite` attribute of the cookie. The default value is [`SameSite::Strict`].
///
/// See: [SameSite Cookie Attribute](https://owasp.org/www-community/SameSite).
Expand Down Expand Up @@ -182,6 +204,33 @@ where
request.extensions_mut().insert(self.config.clone());
request.extensions_mut().insert(token);

Box::pin(self.inner.call(request))
let config = self.config.clone();

if config.hsts {
let future = self.inner.call(request);

Box::pin(async move {
let mut response = future.await?;

let mut value = "max-age=31536000; includeSubDomains".to_owned();

if config.preload {
value.push_str("; preload");
}

let value = match HeaderValue::from_str(&value) {
Ok(value) => value,
Err(err) => return Error::make_layer_error(err),
};

response
.headers_mut()
.insert("Strict-Transport-Security", value);

Ok(response)
})
} else {
Box::pin(self.inner.call(request))
}
}
}

0 comments on commit 36518c4

Please sign in to comment.