如何在TLSv1.3握手时检测缺失ALPN并终止握手?
TLSv1.3服务器强制ALPN验证的问题
核心需求
客户端发起TLSv1.3连接时,必须在Client Hello中携带包含特定条目的ALPN扩展;若未携带,需立即终止握手并发送TLS致命告警。
当前技术栈
使用C++结合Boost.Asio v1.83与OpenSSL 3.1搭建TLSv1.3服务器,核心异步操作依赖:
boost::asio::async_accept(...); boost::asio::async_handshake(...); boost::asio::async_read_some(...); boost::asio::async_write(...); boost::asio::async_shutdown(...);
现有ALPN验证逻辑(正常场景)
当客户端Client Hello包含ALPN扩展时,OpenSSL会触发自定义的alpn_select_cb回调,在此可完成ALPN校验,根据结果返回对应状态终止握手,该逻辑运行正常:
// 基于SSL_select_next_proto实现ALPN协商 int ServerSession::alpn_select_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { ServerSession* srvSession = static_cast<ServerSession*>(arg); int status = SSL_select_next_proto(const_cast<unsigned char **>(out), outlen, srvSession->m_serverAlpnList.data(), srvSession->m_serverAlpnList.size(), in, inlen); switch (status) { case OPENSSL_NPN_NEGOTIATED: return SSL_TLSEXT_ERR_OK; // ALPN协商成功 case OPENSSL_NPN_UNSUPPORTED: return SSL_TLSEXT_ERR_NOACK; // ALPN不被支持,返回告警 case OPENSSL_NPN_NO_OVERLAP: return SSL_TLSEXT_ERR_ALERT_FATAL; // 无匹配ALPN,发送致命告警终止握手 } }
此场景下,Wireshark抓包显示服务器会正常发送致命告警,握手终止符合预期。
异常场景问题
当客户端未携带ALPN扩展时,OpenSSL不会触发alpn_select_cb回调,握手会完整完成,之后只能在服务器端主动关闭连接,抓包流程如下:
No. Time Source Destination Protocol Length Info --------------------------------------------------------------------------------------------------------------------------- 24 18.605087 127.0.0.1 127.0.0.5 TLSv1.3 273 Client Hello --> 客户端发起无ALPN的TLSv1.3连接 26 18.607975 127.0.0.5 127.0.0.1 TLSv1.3 1479 Server Hello, Change Cipher Spec, Application Data, Application Data, Application Data, Application Data 28 18.609183 127.0.0.1 127.0.0.5 TLSv1.3 124 Change Cipher Spec, Application Data 30 18.612081 127.0.0.5 127.0.0.1 TLSv1.3 554 Application Data, Application Data --> 握手完成 32 18.613404 127.0.0.5 127.0.0.1 TLSv1.3 68 Application Data --> 服务器发送close_notify关闭连接 34 22.126810 127.0.0.1 127.0.0.5 TLSv1.3 71 Application Data --> 客户端同时尝试发送数据 36 22.127768 127.0.0.1 127.0.0.5 TLSv1.3 68 Application Data --> 客户端发送close_notify
这种流程不符合需求,需要在握手阶段直接终止并发送致命告警。
尝试的解决方案(无效)
尝试使用OpenSSL的消息回调cb_ssl_msg,在处理完Client Hello后检查是否有选中的ALPN,尝试终止握手:
// SSL消息回调,尝试在握手阶段检查ALPN void ServerSession::cb_ssl_msg(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) { if (SSL_get_state(ssl) == TLS_ST_SW_SRVR_HELLO) { // 获取选中的ALPN unsigned int alpnLen; const unsigned char* alpnData; SSL_get0_alpn_selected(ssl, &alpnData, &alpnLen); if (alpnLen == 0) // 无ALPN被选中(客户端未发送ALPN扩展) { // 尝试关闭连接,但无法阻止握手完成 ServerSession* srvSession = static_cast<ServerSession*>(arg); srvSession->m_socket.async_shutdown(boost::bind(&ServerSession::shutdownHandler, srvSession, boost::asio::placeholders::error)); return; } } }
问题:async_shutdown无法中断正在进行的握手流程,握手仍会完成,无法实现需求。
求助
如何在检测到客户端未携带ALPN扩展时,立即终止TLS握手并发送致命告警,行为与ALPN不匹配时一致?
内容的提问来源于stack exchange,提问作者SBond
相关产品推荐
相关产品推荐

