bound session cache

When establishing new TLS sessions, servers may send multiple session
tickets (RFC8446 4.6.1). hyper-boring caches tickets without placing a
limit on how many tickets are cached. This leads to unbounded growth of
hyper-boring's cache and leaves clients vulnerable to malicious servers
who might send many session tickets to exhaust a client's available
memory.

This change bounds the cache to a default of 8 tickets.
This commit is contained in:
Eric Rosenberg 2022-11-28 22:23:37 +00:00 committed by Rushil Mehra
parent 3d9a5e3244
commit 8db6134c75
2 changed files with 26 additions and 12 deletions

View File

@ -40,24 +40,32 @@ impl Borrow<[u8]> for HashSession {
pub struct SessionCache {
sessions: HashMap<SessionKey, LinkedHashSet<HashSession>>,
reverse: HashMap<HashSession, SessionKey>,
/// Maximum capacity of LinkedHashSet per SessionKey
per_key_session_capacity: usize,
}
impl SessionCache {
pub fn new() -> SessionCache {
pub fn with_capacity(per_key_session_capacity: usize) -> SessionCache {
SessionCache {
sessions: HashMap::new(),
reverse: HashMap::new(),
per_key_session_capacity,
}
}
pub fn insert(&mut self, key: SessionKey, session: SslSession) {
let session = HashSession(session);
self.sessions
.entry(key.clone())
.or_default()
.insert(session.clone());
let sessions = self.sessions.entry(key.clone()).or_default();
// if sessions exceed capacity, discard oldest
if sessions.len() >= self.per_key_session_capacity {
if let Some(hash) = sessions.pop_front() {
self.reverse.remove(&hash);
}
}
sessions.insert(session.clone());
self.reverse.insert(session, key);
}

View File

@ -102,8 +102,19 @@ impl HttpsLayer {
/// Creates a new `HttpsLayer`.
///
/// The session cache configuration of `ssl` will be overwritten.
pub fn with_connector(mut ssl: SslConnectorBuilder) -> Result<HttpsLayer, ErrorStack> {
let cache = Arc::new(Mutex::new(SessionCache::new()));
pub fn with_connector(ssl: SslConnectorBuilder) -> Result<HttpsLayer, ErrorStack> {
Self::with_connector_and_capacity(ssl, 8)
}
/// Creates a new `HttpsLayer` with session capacity.
///
/// The session cache configuration of `ssl` will be overwritten. Session capacity is per
/// session key (domain).
pub fn with_connector_and_capacity(
mut ssl: SslConnectorBuilder,
capacity: usize,
) -> Result<HttpsLayer, ErrorStack> {
let cache = Arc::new(Mutex::new(SessionCache::with_capacity(capacity)));
ssl.set_session_cache_mode(SslSessionCacheMode::CLIENT);
@ -116,11 +127,6 @@ impl HttpsLayer {
}
});
ssl.set_remove_session_callback({
let cache = cache.clone();
move |_, session| cache.lock().remove(session)
});
Ok(HttpsLayer {
inner: Inner {
ssl: ssl.build(),