概要
クライアント側の Python や OpenSSL は最新でサーバ側の SSL の設定が古い場合の対処方法を紹介します
環境
- Python 3.11.3
- requests 2.31.0
- OpenSSL 3.0.2 15
エラー詳細
以下のような感じで requests で接続時にハンドシェイクエラーが発生します
requests.exceptions.SSLError: HTTPSConnectionPool(host='xxx.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:997)')))
おそらく原因は requests が最新の ciphers を使用して接続しに行っておりサーバ側がその ciphers に対応しておらずエラーとなっています
対処方法
requests で chipers を指定することができるのでそれを使います
Adapter という機能を使います
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.ssl_ import create_urllib3_context # type: ignore
# ここはサーバ側が対応している ciphers を指定すること
CIPHERS = "AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA"
class RsaAesShaAdapter(HTTPAdapter):
def init_poolmanager(self, *args, **kwargs):
context = create_urllib3_context(ciphers=CIPHERS)
kwargs["ssl_context"] = context
return super(RsaAesShaAdapter, self).init_poolmanager(*args, **kwargs)
def proxy_manager_for(self, *args, **kwargs):
context = create_urllib3_context(ciphers=CIPHERS)
kwargs["ssl_context"] = context
return super(RsaAesShaAdapter, self).proxy_manager_for(*args, **kwargs)
# セッションを作成
s = requests.Session()
# セッションに対して https 通信の場合に指定のアダプタを使用するように設定
s.mount("https://", RsaAesShaAdapter())
r = s.get("https://xxx.com")
print(r.text)
おそらくこれで接続できるようになるはずです
サーバ側が対応している ciphers の調べ方
このあたりのやり方だとコマンドでできます
あとは外部からアクセスできるのであれば SSLChecker などのツールを使うと簡単に対応している ciphers を調べることができます
curl では普通にできる
おそらく curl はサーバ側の ciphers を調べているか最新でダメな場合に ciphers を変えてリトライしているのかもしれません
最後に
なぜか curl だとうまく行くが requests や http.client ではうまくいかない場合があります
そんあ場合は ciphers を見直してみると良いかなと思います
0 件のコメント:
コメントを投稿