urllib 2.x版本下dh key too small 等SSL类错误的解决方法

发布时间 2023-11-07 17:32:49作者: 写python的叮叮叮

问题

当我们使用python的requests访问目标网站的时候,有时会遇到以下这种错误(以 https://dh-composite.badssl.com/ 为例)

requests.exceptions.SSLError: HTTPSConnectionPool(host='dh-composite.badssl.com', port=443): Max retries exceeded with url: /
(Caused by SSLError(SSLError(1, '[SSL: DH_KEY_TOO_SMALL] dh key too small (_ssl.c:992)')))

这个时候我们在网上搜索解决办法往往是这种解决办法

import requests
requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = 'DEFAULT:@SECLEVEL=1'

# 或者
import urllib3
urllib3.util.ssl_.DEFAULT_CIPHERS = 'DEFAULT:@SECLEVEL=1'

但是在某些情况下我们发现是仍然没办法生效,或者直接提示没有DEFAULT_CIPHERS这个属性的。
这是因为urllib3在2023年4月升级到了2.0.0版本,并且在新版本中废弃了DEFAULT_CIPHERS属性,所以我们需要另辟蹊径。

原因

具体原因可以看这个回答
简单来说DH_KEY_TOO_SMALL的本质原因是目标网站的SECLEVEL设置为1,而这被认为是不安全的,所以访问会报错。解决办法就是强制将SECLEVEL设置为1或者0。在1.x的urllib3中,urllib3提供了DEFAULT_CIPHERS属性来自定义ssl相关的参数,但是在2.x版本中为了安全废弃了该属性。而由于requests实际上是调用urllib3请求网站,所以同样会出现这样的错误。

解决办法

在urllib3的issue中有位大佬提供了解决办法,也就是自定义HTTPAdapter的实例
以下是代码

import requests
import urllib3

# 自定义HTTPAdapter
class CustomSSLContextHTTPAdapter(requests.adapters.HTTPAdapter):
    def __init__(self, ssl_context=None, **kwargs):
        self.ssl_context = ssl_context
        super().__init__(**kwargs)

    def init_poolmanager(self, connections, maxsize, block=False):
        self.poolmanager = urllib3.poolmanager.PoolManager(
            num_pools=connections, maxsize=maxsize,
            block=block, ssl_context=self.ssl_context)

#自定义ssl_context
ctx = urllib3.util.create_urllib3_context()
ctx.load_default_certs()
ctx.set_ciphers("DEFAULT@SECLEVEL=0")

#替换原有的https的adapters
session = requests.session()
session.adapters.pop("https://", None)
session.mount("https://", CustomSSLContextHTTPAdapter(ssl_context=ctx))

response = requests.request(
    method= "GET",
    url='https://dh-composite.badssl.com/',
)
print(response.content)

这样就可以成功的请求目标网站了。同理对于其他ssl的错误也可以举一反三通过这样的方式更改ciphers的参数来进行解决。