如何在TLS通信时在客户端与服务端打印协商的TLS版本
实现客户端(urllib3)与服务端(CherryPy)协商TLS版本的日志打印
我们需要在客户端(基于urllib3)和服务端(基于CherryPy)的日志中,打印TLS握手协商出的具体版本。以下是具体实现方案:
客户端(urllib3)实现
urllib3的连接对象在完成TLS握手后,可通过底层socket的version()方法获取协商的TLS版本。在发送请求后,直接从连接实例中提取该信息并打印即可。
修改后的客户端代码示例:
self.__connection = ProxyManager(proxy_url=proxy_url, proxy_headers=self.__get_proxy_headers(), proxy_ssl_context=self.__get_proxy_ssl_context(proxy_url), cert_file=cert_file, key_file=cert_key, key_password=key_pwd, cert_reqs=cert_reqs, ca_certs=ca_cert, timeout=self._get_timeout(connect_timeout=self.__connect_timeout, read_timeout=self.__read_timeout), ssl_context=self.__get_ssl_context(), retries=False) def __get_ssl_context(): ssl_context = create_urllib3_context(ssl_version=ssl.PROTOCOL_TLS_CLIENT, ciphers=constants.SSL_TLS_CIPHER_SUITE) ssl_context.set_ecdh_curve(constants.SSL_TLS_CURVE) ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2 return ssl_context # 发送请求后打印协商的TLS版本 def send_request_and_log_tls_version(self, url, method="GET", **kwargs): response = self.__connection.request(method, url, **kwargs) # 获取协商的TLS版本 tls_version = self.__connection.sock.version() print(f"客户端协商的TLS版本: {tls_version}") # 可替换为项目日志组件,如logging.info(f"客户端协商的TLS版本: {tls_version}") return response
说明:
- 调用
self.__connection.sock.version()可直接获取版本标识,返回值如TLSv1.2或TLSv1.3 - 可根据项目日志规范替换
print为对应日志库调用
服务端(CherryPy)实现
CherryPy的SSL适配器在完成socket包裹后,可通过socket对象获取协商的TLS版本。提供两种实现方案:
方案1:重写SSLAdapter的wrap_socket方法
在TLS握手完成后立即打印版本,适合全局监控:
cheroot.server.ssl_adapters['ssl-password-adapter'] = SSLPasswordAdapter class SSLPasswordAdapter(BuiltinSSLAdapter): def __init__(self, certificate, private_key, certificate_chain=None, ciphers=None): self.certificate = certificate self.private_key = private_key self.certificate_chain = certificate_chain self.ciphers = ciphers self.context = ssl.create_default_context( purpose=ssl.Purpose.CLIENT_AUTH, cafile=certificate_chain, ) self.context.minimum_version = ssl.TLSVersion.TLSv1_2 self.context.load_cert_chain(certificate, private_key, self._password) if self.ciphers is not None: self.context.set_ciphers(ciphers) else: self.context.set_ciphers(constants.SSL_TLS_CIPHER_SUITE) self.context.set_ecdh_curve(constants.SSL_TLS_CURVE) def wrap_socket(self, sock, server_side=False, **kwargs): wrapped_sock = super().wrap_socket(sock, server_side=True, **kwargs) # 获取协商的TLS版本 tls_version = wrapped_sock.version() print(f"服务端与客户端协商的TLS版本: {tls_version}") # 可替换为项目日志组件,如logging.info(f"服务端与客户端协商的TLS版本: {tls_version}") return wrapped_sock
方案2:通过CherryPy请求钩子获取
在请求处理阶段获取版本,可关联具体请求上下文:
import cherrypy def log_tls_version(): # 获取当前请求的底层socket sock = cherrypy.request.rfile.raw._sock if hasattr(sock, 'version'): tls_version = sock.version() print(f"服务端处理请求时协商的TLS版本: {tls_version}") # 可替换为项目日志组件,如logging.info(f"服务端处理请求时协商的TLS版本: {tls_version}") # 注册请求钩子,在请求开始时执行 cherrypy.tools.log_tls = cherrypy.Tool('on_start_resource', log_tls_version) # 启用该工具 cherrypy.config.update({'tools.log_tls.on': True})
说明:
- 若
version()方法无法调用,可尝试sock._sslobj.version()(适配部分Python/CherryPy版本) - 方案1侧重全局握手监控,方案2可结合请求参数做精细化日志记录
内容的提问来源于stack exchange,提问作者dev
相关产品推荐
相关产品推荐

