Skip to content

Commit

Permalink
fix(server): host filter via URI read authority (#1178)
Browse files Browse the repository at this point in the history
It is possible that the HOST is sent via the URI and when that is the case
the server should read the `Authority (host:port)` not only the host
  • Loading branch information
niklasad1 committed Aug 10, 2023
1 parent 3bd2d20 commit 55010af
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 10 deletions.
14 changes: 5 additions & 9 deletions server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use std::time::Duration;

use crate::future::{ConnectionGuard, ServerHandle, StopHandle};
use crate::logger::{Logger, TransportProtocol};
use crate::transport::http::fetch_authority;
use crate::transport::{http, ws};

use futures_util::future::{self, Either, FutureExt};
Expand All @@ -44,7 +45,7 @@ use jsonrpsee_core::id_providers::RandomIntegerIdProvider;

use jsonrpsee_core::server::{AllowHosts, Authority, AuthorityError, Methods, WhitelistedHosts};
use jsonrpsee_core::traits::IdProvider;
use jsonrpsee_core::{http_helpers, Error, TEN_MB_SIZE_BYTES};
use jsonrpsee_core::{Error, TEN_MB_SIZE_BYTES};

use soketto::handshake::http::is_upgrade_request;
use tokio::net::{TcpListener, TcpStream, ToSocketAddrs};
Expand Down Expand Up @@ -651,16 +652,11 @@ impl<L: Logger> hyper::service::Service<hyper::Request<hyper::Body>> for TowerSe
fn call(&mut self, request: hyper::Request<hyper::Body>) -> Self::Future {
tracing::trace!("{:?}", request);

let host = match http_helpers::read_header_value(request.headers(), hyper::header::HOST) {
Some(host) => host,
None if request.version() == hyper::Version::HTTP_2 => match request.uri().host() {
Some(host) => host,
None => return async move { Ok(http::response::malformed()) }.boxed(),
},
None => return async move { Ok(http::response::malformed()) }.boxed(),
let Some(authority) = fetch_authority(&request) else {
return async { Ok(http::response::malformed()) }.boxed();
};

if let Err(e) = self.inner.allow_hosts.verify(host) {
if let Err(e) = self.inner.allow_hosts.verify(authority) {
tracing::debug!("Denied request: {}", e);
return async { Ok(http::response::host_not_allowed()) }.boxed();
}
Expand Down
16 changes: 15 additions & 1 deletion server/src/transport/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use futures_util::future::Either;
use futures_util::stream::{FuturesOrdered, StreamExt};
use hyper::Method;
use jsonrpsee_core::error::GenericTransportError;
use jsonrpsee_core::http_helpers::read_body;
use jsonrpsee_core::http_helpers::{self, read_body};
use jsonrpsee_core::server::helpers::{
batch_response_error, prepare_error, BatchResponseBuilder, MethodResponse, MethodResponseResult,
};
Expand Down Expand Up @@ -50,6 +50,20 @@ pub(crate) async fn reject_connection(socket: tokio::net::TcpStream) {
}
}

/// The `Authority` can be sent by the client in the `Host header` or in the `URI`
/// such that we must check both.
pub(crate) fn fetch_authority(request: &hyper::Request<hyper::Body>) -> Option<&str> {
let host_header = http_helpers::read_header_value(request.headers(), hyper::header::HOST);
let uri = request.uri().authority();

match (host_header, uri) {
(Some(a1), Some(a2)) if a1 == a2.as_str() => Some(a1),
(Some(a), None) => Some(a),
(None, Some(a)) => Some(a.as_str()),
_ => None,
}
}

#[derive(Debug)]
pub(crate) struct ProcessValidatedRequest<'a, L: Logger> {
pub(crate) request: hyper::Request<hyper::Body>,
Expand Down

0 comments on commit 55010af

Please sign in to comment.