index : static-web-server.git

ascending towards madness

author Jose Quintana <joseluisquintana20@gmail.com> 2021-06-11 0:41:00.0 +00:00:00
committer Jose Quintana <joseluisquintana20@gmail.com> 2021-06-11 0:41:00.0 +00:00:00
commit
c96af53fb6d80a985da926baadbf17ca94fef40d [patch]
tree
9e85b3378e92f7528d17b55a812bc0b1a3b9240b
parent
5343a2205e75201480b953531e1ca1b61b9b0766
download
c96af53fb6d80a985da926baadbf17ca94fef40d.tar.gz

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'

Diff

 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(-)

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<Arc<cors::Configured>>,
    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<Body>) {
    // 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,
            },
        });