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:
parent
3d9a5e3244
commit
8db6134c75
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
Loading…
Reference in New Issue