OpenSSL内部客户端会话缓存除触发会话回调外有哪些合法用途?
以下为原提问内容的准确表述:
我正在开发支持TLSv1.3的SSL客户端应用,为实现会话复用能力,通过SSL_CTX_sess_set_new_cb注册新会话回调,OpenSSL与服务端协商生成新会话时会触发该回调。回调逻辑中,我会将生成的会话存入应用自建的会话缓存,供后续连接复用。
根据OpenSSL的运行机制,只有启用内部客户端会话缓存时,注册的上述回调才会被触发,启用方式为:
SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_CLIENT);
目前遇到的核心问题是存储冗余:应用自建的外部缓存与OpenSSL内部缓存会存储完全相同的会话数据,存在不必要的重复。我希望消除这种冗余,但两套缓存目前都无法舍弃:
- 受OpenSSL机制限制,必须开启内部缓存才能触发注册的会话新建回调
- 外部缓存是应用检索会话、通过
SSL_set_session完成会话复用的唯一途径
这也是使用OpenSSL内部客户端会话缓存时的通用典型问题。根据SSL_CTX_set_session_cache_mode手册中SSL_SESS_CACHE_CLIENT章节的说明:
由于抽象BIO层的隔离,SSL引擎无法获取连接的具体业务细节,OpenSSL库无法可靠判断客户端侧的会话是否应当被复用、应当选择哪一个会话复用,因此应用必须通过
SSL_set_session(3)自行选择待复用的会话。
我对机制的理解如下:
- 客户端应用必须自行完成会话选择逻辑,向OpenSSL提供明确的
SSL_SESSION*实例才能完成会话复用。也就是说,即便会话已经存在于OpenSSL的内部缓存中,OpenSSL也不会自行决策是否复用客户端侧缓存的会话。 - 因此,实际用于复用的会话指针必须来自应用的外部缓存或独立存储,而非OpenSSL的内部客户端会话缓存。
基于这个机制,我的疑问是:除了作为触发会话新建回调的前置条件之外,启用OpenSSL内部客户端会话缓存还有哪些合理的使用场景?比如,是否存在启用内部缓存但完全不需要外部缓存的合理应用场景?是否存在不使用会话回调函数时启用内部缓存的合理场景?以上问题仅针对客户端应用场景探讨。
问题解答
冗余存储的解决方案
你遇到的双缓存冗余问题完全可以解决,不需要同时保留两套存储:
启用SSL_SESS_CACHE_CLIENT满足回调触发要求后,可以在新会话回调中,通过SSL_CTX_remove_session接口,将刚生成的会话从OpenSSL内部缓存中移除。由于回调触发时你已经将会话指针存入外部缓存,持有了会话的有效引用,这个移除操作不会导致会话被提前释放,最终OpenSSL内部缓存的条目数会始终保持为0,完全不会产生重复存储的内存开销,既满足了回调触发的前置条件,又消除了冗余。
客户端内部缓存的合法使用场景
抛开“作为回调触发前置条件”的作用,客户端内部缓存存在两类明确的设计使用场景:
- 轻量应用的零开发成本会话存储:对于不需要自定义缓存策略、不需要跨进程/跨连接长期留存会话的简单客户端应用,完全不需要自建外部缓存。启用内部缓存后,OpenSSL会自动完成会话的生命周期管理、过期清理、内存淘汰逻辑,应用只需要在发起新连接前,通过OpenSSL提供的内部缓存遍历接口取出匹配的会话指针,手动调用
SSL_set_session绑定到新连接上即可,省去了自行实现哈希缓存、过期清理、内存淘汰的开发成本。 - 会话票据的安全校验载体:无论是TLS1.2还是TLS1.3,服务端下发的会话票据都需要客户端侧做有效性校验、重放防护,OpenSSL会自动把与票据关联的会话条目存入内部客户端缓存,作为校验逻辑的基础存储。即使你完全不使用内部缓存的条目做会话复用,这个存储环节也是OpenSSL实现会话安全校验的必要步骤,缺失会导致票据校验失败、会话复用被服务端拒绝。
无自定义回调场景下的内部缓存价值
如果完全不使用会话新建回调,启用内部客户端缓存的核心价值是给缺乏缓存实现能力的应用提供开箱即用的会话存储能力,不需要应用介入会话的存储、清理逻辑,仅需完成最核心的“取出会话指针、绑定到新连接”的步骤,就能实现基本的会话复用能力。这类场景下,应用不需要维护独立的外部缓存,OpenSSL内部缓存就是唯一的会话存储来源。
内容的提问来源于stack exchange,提问作者Craig M. Brandenburg

