2024年2月21日水曜日

Python の requests で sslv3 alert handshake failure

Python の requests で sslv3 alert handshake failure

概要

クライアント側の 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 件のコメント:

コメントを投稿