Skip to content

Commit

Permalink
feat: Add security headers (#58)
Browse files Browse the repository at this point in the history
* feat: Add security headers

Issue #19
  • Loading branch information
jrconlin authored Nov 14, 2018
1 parent cd44e85 commit 8a70fff
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 13 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion channelserver/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "channelserver"
version = "0.7.1"
version = "0.8.0"
authors = ["jr conlin<src+pairsona@jrconlin.com"]

[dependencies]
Expand Down
53 changes: 44 additions & 9 deletions channelserver/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@ extern crate maxminddb;
extern crate reqwest;

use std::path::Path;
use std::str;
use std::time::{Duration, Instant};

use actix::Arbiter;
use actix_web::dev::HttpResponseBuilder;
use actix_web::server::HttpServer;
use actix_web::{fs, http, ws, App, Error, HttpRequest, HttpResponse};
use actix_web::{fs, http, ws, App, AsyncResponder, Error, HttpMessage, HttpRequest, HttpResponse};
use futures::Future;

mod channelid;
mod logging;
Expand Down Expand Up @@ -77,7 +80,9 @@ fn channel_route(req: &HttpRequest<session::WsChannelSessionState>) -> Result<Ht
warn!(&req.state().log.log,
"Invalid ChannelID specified: {:?}", id;
"remote_ip" => &meta_info.remote);
return Ok(HttpResponse::new(http::StatusCode::NOT_FOUND));
let mut resp =
HttpResponse::new(http::StatusCode::NOT_FOUND).into_builder();
return Ok(add_headers(&mut resp).finish());
}
};
channel_id
Expand All @@ -93,6 +98,7 @@ fn channel_route(req: &HttpRequest<session::WsChannelSessionState>) -> Result<Ht
"remote_ip" => &meta_info.remote
);

// Cannot apply headers here.
ws::start(
req,
session::WsChannelSession {
Expand All @@ -106,24 +112,50 @@ fn channel_route(req: &HttpRequest<session::WsChannelSessionState>) -> Result<Ht
)
}

/// Inject the FoxSec headers into all HTTP responses
fn add_headers<'a>(resp: &'a mut HttpResponseBuilder) -> &'a mut HttpResponseBuilder {
resp.header(
"Content-Security-Policy",
"default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; report-uri /__cspreport__",
)
.header("X-Content-Type-Options", "nosniff")
.header("X-Frame-Options", "deny")
.header("X-XSS-Protection", "1; mode=block")
.header("Strict-Transport-Security", "max-age=63072000")
}

fn heartbeat(req: &HttpRequest<session::WsChannelSessionState>) -> Result<HttpResponse, Error> {
// if there's more to check, add it here.
let body = json!({"status": "ok", "version": env!("CARGO_PKG_VERSION")});
Ok(HttpResponse::Ok()
.content_type("application/json")
.body(body.to_string()))
Ok(add_headers(HttpResponse::Ok().content_type("application/json")).body(body.to_string()))
}

fn lbheartbeat(req: &HttpRequest<session::WsChannelSessionState>) -> Result<HttpResponse, Error> {
// load balance heartbeat. Doesn't matter what's returned, aside from a 200
Ok(HttpResponse::Ok().into())
Ok(add_headers(&mut HttpResponse::Ok()).finish())
}

fn show_version(req: &HttpRequest<session::WsChannelSessionState>) -> Result<HttpResponse, Error> {
// Return the contents of the version.json file.
Ok(HttpResponse::Ok()
.content_type("application/json")
.body(include_str!("../version.json")))
Ok(
add_headers(&mut HttpResponse::Ok().content_type("application/json"))
.body(include_str!("../version.json")),
)
}

/// Dump the "CSP report" as a warning message.
fn cspreport(
req: &HttpRequest<session::WsChannelSessionState>,
) -> Box<Future<Item = HttpResponse, Error = Error>> {
let log = req.state().log.clone();
req.body()
.from_err()
.and_then(move |body| {
let bstr = str::from_utf8(&body).unwrap();
warn!(log.log, "CSP Report"; "report"=> bstr);
Ok(add_headers(&mut HttpResponse::Ok()).finish())
})
.responder()
}

fn build_app(app: App<session::WsChannelSessionState>) -> App<session::WsChannelSessionState> {
Expand All @@ -140,6 +172,9 @@ fn build_app(app: App<session::WsChannelSessionState>) -> App<session::WsChannel
})
.resource("/__lbheartbeat__", |r| {
r.method(http::Method::GET).f(lbheartbeat)
})
.resource("/__cspreport__", |r| {
r.method(http::Method::POST).f(cspreport)
});
// Only add a static handler if the static directory exists.
if Path::new("static/").exists() {
Expand Down
4 changes: 2 additions & 2 deletions channelserver/src/perror.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ pub struct HandlerError {

#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum HandlerErrorKind {
#[fail(display = "Excess Data Exchanged: _0")]
#[fail(display = "Excess Data Exchanged: {:?}", _0)]
XSDataErr(String),
#[fail(display = "Excess Messages: _0")]
#[fail(display = "Excess Messages: {:?}", _0)]
XSMessageErr(String),
#[fail(display = "IO Error: {:?}", _0)]
IOError(String),
Expand Down

0 comments on commit 8a70fff

Please sign in to comment.