python请求https出现版本异常的问题
背景
场景
import requests
r = requests.get("https://xxxx/wholeprocess/health")
print(r)
Client报错
requests.exceptions.SSLError: HTTPSConnectionPool(host='xxx', port=xxx): Max retries exceeded with url: /wholeprocess/health (Caused by SSLError(SSLError(1, '[SSL: UNKNOWN_PROTOCOL] unknown protocol (_ssl.c:1056)')))
排查
使用curl排除代码问题
但是依然出现这个问题,而且是偶发的
xxxxx-MB0 /tmp % curl https://xxxxxx/wholeprocess/health
curl: (35) error:1400410B:SSL routines:CONNECT_CR_SRVR_HELLO:wrong version number
堆栈
Traceback (most recent call last):
File "xxxx/venv/lib/python3.7/site-packages/urllib3/connectionpool.py", line 706, in urlopen
chunked=chunked,
File "xxxx/venv/lib/python3.7/site-packages/urllib3/connectionpool.py", line 382, in _make_request
self._validate_conn(conn)
File "xxxx/venv/lib/python3.7/site-packages/urllib3/connectionpool.py", line 1010, in _validate_conn
conn.connect()
File "xxxx/venv/lib/python3.7/site-packages/urllib3/connection.py", line 421, in connect
tls_in_tls=tls_in_tls,
File "xxxx/venv/lib/python3.7/site-packages/urllib3/util/ssl_.py", line 432, in ssl_wrap_socket
ssl_sock = _ssl_wrap_socket_impl(sock, context, tls_in_tls)
File "xxxx/venv/lib/python3.7/site-packages/urllib3/util/ssl_.py", line 474, in _ssl_wrap_socket_impl
return ssl_context.wrap_socket(sock)
File "/usr/local/lib/python3.7/ssl.py", line 412, in wrap_socket
session=session
File "/usr/local/lib/python3.7/ssl.py", line 853, in _create
self.do_handshake()
File "/usr/local/lib/python3.7/ssl.py", line 1117, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: UNKNOWN_PROTOCOL] unknown protocol (_ssl.c:1056)
do_handshake()在做什么?
进行SSL协议的握手过程,握手成功后才可以进行加密通信
nginx
xxxxx - - [05/Sep/2021:16:05:30 +0800] "\x16\x03\x01\x00\xDD\x01\x00\x00\xD9\x03\x03k\xA0\xA8\xAAX\xAB\xFA\x9D\xA5yg\x92\xCF\xD5}\xD5\xF7P\x8D\xE6\xEA\x16\xB3\xCD\xA3\x167~c\xE2ef\x00\x00b\xC00\xC0,\xC0/\xC0+\x00\x9F\x00\x9E\xC02\xC0.\xC01\xC0-\x00\xA5\x00\xA1\x00\xA4\x00\xA0\xC0(\xC0$\xC0\x14\xC0" 400 157 "-" "-" "-"
Server报错
[2021-09-05 16:03:07][ERROR][web_protocol.py:393][26393][Error handling request]
Traceback (most recent call last):
File "/xxxxx/venv/lib/python3.7/site-packages/aiohttp/web_protocol.py", line 314, in data_received
messages, upgraded, tail = self._request_parser.feed_data(data)
File "aiohttp/_http_parser.pyx", line 546, in aiohttp._http_parser.HttpParser.feed_data
aiohttp.http_exceptions.BadStatusLine: 400, message="Bad status line 'invalid HTTP method'"
原因
1.使用https协议请求http接口
在服务端或者LB上很容易能看到问题
This means that if your client is trying to start the TLS handshake needed for https against this port it will get a plain error message back. The client then tries to interpret this error message as TLS and returns some weird error messages, because the response is not TLS at all.
这意味着,如果您的客户端尝试针对此端口启动 https 所需的 TLS 握手,它将收到一条简单的错误消息。然后客户端尝试将此错误消息解释为 TLS 并返回一些奇怪的错误消息,因为响应根本不是 TLS
(Go里面却提示很好,Post "https://ses.test.tencentcloudapi.com": http: server gave HTTP response to HTTPS client,python里面应该是直接沿用curl命令的提示)
2.后端多节点,部分开启https,导致偶发
https通信过程
Client Hello
第一次由客户端发起请求,内容包含客户端支持的加密套件【Cipher Suites】,同时生成一个随机数【Random】
Server Hello
服务端收到后从加密套件中选出一个自己支持的加密套件,如果有返回一个随机数【Random】以及加密套件【Cipher Suites】,否则返回报错
Certifate , Server Key Exchange , Server Hello Done
服务端发起
Certifate: 向客户端发动 CA认证的数字证书,鉴别服务端身份
Server Hello Done: 服务端宣告和客户端第一阶段握手协议结束
Server Key Exhchange[可选]: 如果CA的认证的数字证书提供的信息不够,服务端发送补充信息
Client Key Exchange,Change Cipher Spec ,Encrypted Handshake Message
客户端发起
Client Key Exhchange : 客户端收到CA数字证书并通过认证,然后通过CA公钥解密获取到服务端公钥。Client Key Exchange 报文包含一个随机数,这个随机数成为pre-master key /secret.表示随后的信息使用双方协议好的加密方法密钥发送的通知。
还有一个协商好的hash算法对前面的所有内容信息的hash计算值。用来提供服务端校验。这些信息通过服务端公钥加密传送给服务端。
Change Cipher Spec: 该报文通知服务端,此后的通信将通过协商好的加密算法计算对称密钥,进行加密通信。【使用两个随机数以及第三个 pre-master key/secret 随机数 计算出一个对称密钥 session key / secret 】后续会发送 密钥信息。
Encrypted Handshake Message:客户端或者服务器发送的,属于TLS handshake,也是紧跟着Key Exchange发送。这里是进行一下测试,一方用自己的刚刚生成的密钥加密一段固定的消息发送给对方,如果密钥协商正确无误的话,对方应该可以解密。这段加密内容的明文一般是协议中规定好的,双方都知道
Change Cipher Spec
服务端发起,通知客户端。此后的通信按照协商好的“对称密钥”进行加密通信
参考链接
远程端不是在说 SSL:它在说纯文本。使用浏览器检查http://homebrew.bintray.com:443/。
如果您向 HTTPS 端口发送 HTTP 请求,这是 CloudFront 的正常行为。尝试使用“ http://aws.amazon.com:443 ”实现相同的行为(在 cURL 中,在浏览器中,它可能会通过 HSTS 自动将您升级到 HTTPS)。
@ayypril,虽然这是正常行为,但当端点识别并提供 HTTP 和 HTTPS 时,它会起作用。在这种情况下,似乎由于配置错误,HTTPS 已被禁用,目前仅启用了 HTTP。
这是一个非常相似的问题 - kubernetes/ingress-nginx#3556
100.106.0.0 - [100.106.0.0] - - [12/Dec/2018:13:01:39 +0000] "\x16\x03\x01\x00\xDE\x01\x00\x00\xDA\x03\x03 xAB\x9E\x19\xC2\xE3\xE3\xC6bJy\x80\xCA\x0F^\x7F<\xF5\x0B/t\x1CG\x9Fh}\xDD\xB6aJ\x98\xAE\xBC\x00\x00T\x \xA9\xCC\xA8\xCC\xAA\xC00\xC0,\xC0(\xC0$\xC0\x14\xC0" 400 157 "-" "-" 0 0.045 [] - - - 9420db1697d32fc176504dbf这意味着您将 TCP 内容 (HTTPS) 发送到 HTTP 端口。你在用ELB吗?请检查端口映射