From c96af53fb6d80a985da926baadbf17ca94fef40d Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Fri, 11 Jun 2021 02:41:00 +0200 Subject: [PATCH] feat: security headers for http/2 by default it enables security headers by default when HTTP/2 feature is activated. headers included: - Strict-Transport-Security: max-age=63072000; includeSubDomains; preload (2 years max-age) - X-Frame-Options: DENY - X-XSS-Protection: 1; mode=block - Content-Security-Policy: frame-ancestors 'self' --- src/config.rs | 13 +++++++++++++ src/handler.rs | 8 +++++++- src/lib.rs | 1 + src/security_headers.rs | 35 +++++++++++++++++++++++++++++++++++ src/server.rs | 7 ++++++- 5 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 src/security_headers.rs diff --git a/src/config.rs b/src/config.rs index 5966f1a..b94ca73 100644 --- a/src/config.rs +++ b/src/config.rs @@ -108,4 +108,17 @@ pub struct Config { )] /// Enable directory listing for all requests ending with the slash character (‘/’). pub directory_listing: bool, + + #[structopt( + long, + parse(try_from_str), + required_if("http2", "true"), + default_value_if("http2", Some("true"), "true"), + default_value = "false", + env = "SERVER_SECURITY_HEADERS" + )] + /// Enable security headers by default when HTTP/2 feature is activated. + /// Headers included: "Strict-Transport-Security: max-age=63072000; includeSubDomains; preload" (2 years max-age), + /// "X-Frame-Options: DENY", "X-XSS-Protection: 1; mode=block" and "Content-Security-Policy: frame-ancestors 'self'". + pub security_headers: bool, } diff --git a/src/handler.rs b/src/handler.rs index e6f643a..c755d46 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -2,7 +2,7 @@ use http::StatusCode; use hyper::{Body, Request, Response}; use std::{future::Future, path::PathBuf, sync::Arc}; -use crate::{compression, control_headers, cors, static_files}; +use crate::{compression, control_headers, cors, security_headers, static_files}; use crate::{error_page, Error, Result}; // It defines options for a request handler. @@ -11,6 +11,7 @@ pub struct RequestHandlerOpts { pub compression: bool, pub dir_listing: bool, pub cors: Option>, + pub security_headers: bool, } // It defines the main request handler for Hyper service request. @@ -50,6 +51,11 @@ impl RequestHandler { .await { Ok(mut resp) => { + // Append Security Headers + if self.opts.security_headers { + security_headers::with_security_headers(&mut resp); + } + // Auto compression based on the `Accept-Encoding` header if self.opts.compression { resp = compression::auto(method, headers, resp)?; diff --git a/src/lib.rs b/src/lib.rs index 1b3d95c..b1b4ae7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ pub mod error_page; pub mod handler; pub mod helpers; pub mod logger; +pub mod security_headers; pub mod server; pub mod service; pub mod signals; diff --git a/src/security_headers.rs b/src/security_headers.rs new file mode 100644 index 0000000..c9f82f5 --- /dev/null +++ b/src/security_headers.rs @@ -0,0 +1,35 @@ +use http::header::{ + CONTENT_SECURITY_POLICY, STRICT_TRANSPORT_SECURITY, X_CONTENT_TYPE_OPTIONS, X_FRAME_OPTIONS, + X_XSS_PROTECTION, +}; +use hyper::{Body, Response}; + +/// It appends security headers like `Strict-Transport-Security: max-age=63072000; includeSubDomains; preload` (2 years max-age), +///`X-Frame-Options: DENY`, `X-XSS-Protection: 1; mode=block` and `Content-Security-Policy: frame-ancestors 'self'`. +pub fn with_security_headers(resp: &mut Response) { + // Strict-Transport-Security (HSTS) + resp.headers_mut().insert( + STRICT_TRANSPORT_SECURITY, + "max-age=63072000; includeSubDomains; preload" + .parse() + .unwrap(), + ); + + // X-Frame-Options + resp.headers_mut() + .insert(X_FRAME_OPTIONS, "DENY".parse().unwrap()); + + // X-XSS-Protection + resp.headers_mut() + .insert(X_XSS_PROTECTION, "1; mode=block".parse().unwrap()); + + // X-Content-Type-Options + resp.headers_mut() + .insert(X_CONTENT_TYPE_OPTIONS, "nosniff".parse().unwrap()); + + // Content Security Policy (CSP) + resp.headers_mut().insert( + CONTENT_SECURITY_POLICY, + "frame-ancestors 'self'".parse().unwrap(), + ); +} diff --git a/src/server.rs b/src/server.rs index 5d54942..b0bb735 100644 --- a/src/server.rs +++ b/src/server.rs @@ -89,9 +89,13 @@ impl Server { .set(helpers::read_file_content(opts.page50x.as_ref())) .expect("page 50x is not initialized"); + // Security Headers option + let security_headers = opts.security_headers; + tracing::info!("security headers: enabled={}", security_headers); + // Auto compression based on the `Accept-Encoding` header let compression = opts.compression; - tracing::info!("auto compression compression: enabled={}", compression); + tracing::info!("auto compression: enabled={}", compression); // Directory listing option let dir_listing = opts.directory_listing; @@ -110,6 +114,7 @@ impl Server { compression, dir_listing, cors, + security_headers, }, }); -- libgit2 1.7.2