Skip to content

Instantly share code, notes, and snippets.

@furyutei
Last active February 2, 2024 17:08
Show Gist options
  • Save furyutei/8dfb878a8ca2ed3d5f42853f75612c6f to your computer and use it in GitHub Desktop.
Save furyutei/8dfb878a8ca2ed3d5f42853f75612c6f to your computer and use it in GitHub Desktop.
[Python] requestsのsession使用時にrequests.exceptions.SSLErrorが出てしまうケースへの対応

[Python] requestsのsession使用時にrequests.exceptions.SSLErrorが出てしまうケースへの対応

この例のように

session = requests.session()
response = session.get(TEST_URL)
"""
# TEST_URLのサーバーの設定によっては
# requests.exceptions.SSLError: HTTPSConnectionPool(host='*****', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL: DH_KEY_TOO_SMALL] dh key too small (_ssl.c:992)')))
# のようなエラーが発生してしまう
"""

ごく単純なTEST_URLをget()するだけの処理で、requests.exceptions.SSLErrorが発生する現象に遭遇

解決方法をネットで検索してみると

requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += ":HIGH:!DH"

のようにする方法がひっかかるが、どうもレガシーなバッドノウハウ(?)らしい(urllib3>=2だとDEFAULT_CIPHERSが未定義なので怒られてしまう)。

ssl - How to alter cipher suite used with Python requests? - Stack Overflow こちらの回答を参考にして、

session = requests.session()
session.mount(prefix, custom_adaptor)

のようにURLのprefixに対して(任意のciphersをSSL-contextに適用した)custom_adaptorを設定してやるのが正当な(?)対処方法っぽい。

# -*- coding: utf-8 -*-
import requests
test_url = 'https://*****' # 【テストしたいURLを入れる】
session = requests.session()
print(f'test_url: {test_url}')
response = session.get(test_url)
#requests.exceptions.SSLError: HTTPSConnectionPool(host='*****', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL: DH_KEY_TOO_SMALL] dh key too small (_ssl.c:992)')))
print(f'status_code: {response.status_code}')
# -*- coding: utf-8 -*-
import requests
import re
test_url = 'https://*****' # 【テストしたいURLを入れる】
def get_prefix(target_url): #{
from urllib.parse import urlparse
parts = urlparse(target_url)
return f'{parts.scheme}://{parts.hostname}'
#}
def mount_session_adapter(
session,
prefix,
custom_cipher_list # [SSLCipherSuite Directive](https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslciphersuite)に上がっているようなTag(Cipher Tag/Protocol)のリスト
): #{
"""
sessionでリソースを取得する際、prefixにマッチするURLについては、
カスタマイズされた(custom_cipher_listを設定したSSL-Contextが適用された)アダプタを用いるように設定
参考: [ssl - How to alter cipher suite used with Python requests? - Stack Overflow](https://stackoverflow.com/questions/77262501/how-to-alter-cipher-suite-used-with-python-requests/77270120#77270120)
"""
custom_ciphers = ':'.join(custom_cipher_list)
class CustomCipherAdapter(requests.adapters.HTTPAdapter):
def init_poolmanager(self, *args, **kwargs):
kwargs['ssl_context'] = requests.packages.urllib3.util.ssl_.create_urllib3_context(ciphers=custom_ciphers)
return super(CustomCipherAdapter, self).init_poolmanager(*args, **kwargs)
session.mount(prefix, CustomCipherAdapter())
"""
| mount(self, prefix, adapter)
| Registers a connection adapter to a prefix.
|
| Adapters are sorted in descending order by prefix length.
"""
#}
session = requests.session()
prefix = get_prefix(test_url)
mount_session_adapter(session, prefix, ['HIGH', '!DH', '!aNULL',])
print(f'test_url: {test_url}')
response = session.get(test_url)
print(f'status_code: {response.status_code}')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment