【问题标题】:Python - requests.exceptions.SSLError - dh key too smallPython - requests.exceptions.SSLError - dh 键太小
【发布时间】:2016-10-27 04:47:27
【问题描述】:

我正在使用 Python 和请求抓取一些内部页面。我已关闭 SSL 验证和警告。

requests.packages.urllib3.disable_warnings()
page = requests.get(url, verify=False)

在某些服务器上,我收到一个我无法通过的 SSL 错误。

Traceback (most recent call last):
  File "scraper.py", line 6, in <module>
    page = requests.get(url, verify=False)
  File "/cygdrive/c/Users/jfeocco/VirtualEnv/scraping/lib/python3.4/site-packages/requests/api.py", line 71, in get
    return request('get', url, params=params, **kwargs)
  File "/cygdrive/c/Users/jfeocco/VirtualEnv/scraping/lib/python3.4/site-packages/requests/api.py", line 57, in request
    return session.request(method=method, url=url, **kwargs)
  File "/cygdrive/c/Users/jfeocco/VirtualEnv/scraping/lib/python3.4/site-packages/requests/sessions.py", line 475, in request
    resp = self.send(prep, **send_kwargs)
  File "/cygdrive/c/Users/jfeocco/VirtualEnv/scraping/lib/python3.4/site-packages/requests/sessions.py", line 585, in send
    r = adapter.send(request, **kwargs)
  File "/cygdrive/c/Users/jfeocco/VirtualEnv/scraping/lib/python3.4/site-packages/requests/adapters.py", line 477, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: [SSL: SSL_NEGATIVE_LENGTH] dh key too small (_ssl.c:600)

这发生在 Cygwin 内外、Windows 和 OSX 中。我的研究暗示了服务器上过时的 OpenSSL。我正在寻找理想的修复客户端。

编辑: 我能够通过使用密码集来解决这个问题

import requests

requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += 'HIGH:!DH:!aNULL'
try:
    requests.packages.urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST += 'HIGH:!DH:!aNULL'
except AttributeError:
    # no pyopenssl support used / needed / available
    pass

page = requests.get(url, verify=False)

【问题讨论】:

  • 您在寻找什么样的修复方法?如果 SSL 证书有问题,您要使用 HTTP 还是继续使用 HTTPS 而忽略该问题?
  • @MarcelWilson:这不是证书的问题。
  • @SteffenUllrich 你当然是对的。如果“SSL”通常有问题,我应该说明一下。
  • 将您的代码解决方案移动到答案中将有助于其他人轻松找到解决方案。

标签: python ssl python-requests


【解决方案1】:

禁用警告或证书验证将无济于事。根本问题是服务器使用的弱 DH 密钥可能在Logjam Attack 中被滥用。

要解决此问题,您需要选择一个不使用 Diffie Hellman 密钥交换的密码,因此不受弱 DH 密钥的影响。服务器必须支持此密码。目前尚不清楚服务器支持什么,但您可以尝试使用密码 AES128-SHA 或一组密码 HIGH:!DH:!aNULL

将请求与您自己的密码集一起使用是很棘手的。示例见Why does Python requests ignore the verify parameter?

【讨论】:

  • 感谢您的建议,我能够使用密码集和提供的帖子克服这些错误。
  • 不太擅长这个,我怎么能用你后面的建议用 urllib2 尝试另一个密码,请看看我的问题stackoverflow.com/questions/52440129/…
【解决方案2】:

这不是一个额外的答案,只是尝试将问题中的解决方案代码与额外信息结合起来 所以其他人可以直接复制,无需额外尝试

这不仅仅是服务器端的DH Key问题,还有很多不同的库在python模块中不匹配。

下面的代码段用于忽略这些安全问题,因为它可能无法在服务器端解决。例如,如果它是内部旧版服务器,则没有人愿意更新它。

除了'HIGH:!DH:!aNULL' 的被黑字符串之外,还可以导入urllib3 模块以禁用警告(如果有)

import requests
import urllib3

requests.packages.urllib3.disable_warnings()
requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += ':HIGH:!DH:!aNULL'
try:
    requests.packages.urllib3.contrib.pyopenssl.util.ssl_.DEFAULT_CIPHERS += ':HIGH:!DH:!aNULL'
except AttributeError:
    # no pyopenssl support used / needed / available
    pass

page = requests.get(url, verify=False)

【讨论】:

  • 使用将规则添加到整个 Session 的自定义 HTTPAdapter 更安全,即 requests.Session().mount("affected.website", MySSLContextAdapter(ssl_ciphers='HIGH:!DH:!aNULL ')) 其中适配器创建 context = ssl.create_default_context() 并设置 context.set_ciphers(self.ssl_ciphers) 并在 HTTPAdapter.init_poolmanager 中使用它。
  • 如果您直接与套接字交互,您可以通过上下文设置这些密码(在 Python 3.2+ 和 2.7.9+ 中):context.set_ciphers('HIGH:!DH:!aNULL')
  • 你用这条评论拯救了一个企业。我们用来付款的银行搞砸了他们的 ssl,这暂时节省了一天。给我们足够的时间来改变和与另一家银行合作跨度>
  • 我不需要 'verify=False'。有用。我想说'HIGH:!DH:!aNULL' 的被黑字符串对于在 TLSv1.1 和 TLSv1.2 中弃用 ((Diffie Hellman)) 没有严重的安全问题
【解决方案3】:

我将在这里打包我的解决方案。我不得不修改 python SSL 库,这是可能的,因为我在 docker 容器中运行我的代码,但这可能是你不想做的事情。

  1. 获取您的服务器支持的密码。在我的情况下是第三方电子邮件服务器,我使用了list SSL/TLS cipher suite 描述的脚本

check_supported_ciphers.sh

#!/usr/bin/env bash

# OpenSSL requires the port number.
SERVER=$1
DELAY=1
ciphers=$(openssl ciphers 'ALL:eNULL' | sed -e 's/:/ /g')

echo Obtaining cipher list from $(openssl version).

for cipher in ${ciphers[@]}
do
echo -n Testing $cipher...
result=$(echo -n | openssl s_client -cipher "$cipher" -connect $SERVER 2>&1)
if [[ "$result" =~ ":error:" ]] ; then
  error=$(echo -n $result | cut -d':' -f6)
  echo NO \($error\)
else
  if [[ "$result" =~ "Cipher is ${cipher}" || "$result" =~ "Cipher    :" ]] ; then
    echo YES
  else
    echo UNKNOWN RESPONSE
    echo $result
  fi
fi
sleep $DELAY
done

给它权限:

chmod +x check_supported_ciphers.sh

并执行它:

./check_supported_ciphers.sh myremoteserver.example.com | grep OK

几秒钟后,您将看到类似于以下内容的输出:

Testing AES128-SHA...YES (AES128-SHA_set_cipher_list)

因此将使用“AES128-SHA”作为 SSL 密码。

  1. 强制代码中的错误:

    Traceback(最近一次调用最后一次): 文件“my_custom_script.py”,第 52 行,在 imap = IMAP4_SSL(imap_host) init 中的文件“/usr/lib/python2.7/imaplib.py”,第 1169 行 IMAP4.init(自身、主机、端口) init 中的文件“/usr/lib/python2.7/imaplib.py”,第 174 行 self.open(主机,端口) 文件“/usr/lib/python2.7/imaplib.py”,第 1181 行,打开 self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile) wrap_socket 中的文件“/usr/lib/python2.7/ssl.py”,第 931 行 密码=密码) init 中的文件“/usr/lib/python2.7/ssl.py”,第 599 行 self.do_handshake() do_handshake 中的文件“/usr/lib/python2.7/ssl.py”,第 828 行 self._sslobj.do_handshake() ssl.SSLError: [SSL: DH_KEY_TOO_SMALL] dh 密钥太小 (_ssl.c:727)

  2. 获取使用的python SSL库路径,本例:

    /usr/lib/python2.7/ssl.py

  3. 编辑它:

    cp /usr/lib/python2.7/ssl.py /usr/lib/python2.7/ssl.py.bak

    vim /usr/lib/python2.7/ssl.py

并替换:

_DEFAULT_CIPHERS = (
    'ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:DH+AES256:'
    'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:RSA+AES:RSA+HIGH:'
    '!aNULL:!eNULL:!MD5:!3DES'
    )

作者:

_DEFAULT_CIPHERS = (
    'AES128-SHA'
    )

【讨论】:

  • 根本没有“是”
【解决方案4】:

我遇到了同样的问题。

通过评论修复了它

CipherString = DEFAULT@SECLEVEL=2

/etc/ssl/openssl.cnf 中加入。

【讨论】:

  • 我所做的是将 2 设置为 1,而不是评论孔线,它也可以工作...一定程度的安全总比没有好,对吧?
【解决方案5】:

这也对我有用:

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

openssl SECLEVELs 文档: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_security_level.html

SECLEVEL=2 现在是 openssl 默认值,(至少在我的设置中:ubuntu 20.04,openssl 1.1.1f); SECLEVEL=1 降低标准。

安全级别旨在避免修改单个密码的复杂性。

我相信我们大多数人都没有深入了解单个密码的安全强度/弱点,我当然没有。 安全级别似乎是一种很好的方法来控制您打开安全门的距离。

注意:我得到了一个不同的 SSL 错误,WRONG_SIGNATURE_TYPE 而不是 SSL_NEGATIVE_LENGTH,但根本问题是相同的。

错误:

Traceback (most recent call last):
  [...]
  File "/usr/lib/python3/dist-packages/requests/sessions.py", line 581, in post
    return self.request('POST', url, data=data, json=json, **kwargs)
  File "/usr/lib/python3/dist-packages/requests/sessions.py", line 533, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/lib/python3/dist-packages/requests/sessions.py", line 646, in send
    r = adapter.send(request, **kwargs)
  File "/usr/lib/python3/dist-packages/requests/adapters.py", line 514, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='somehost.com', port=443): Max retries exceeded with url: myurl (Caused by SSLError(SSLError(1, '[SSL: WRONG_SIGNATURE_TYPE] wrong signature type (_ssl.c:1108)')))

【讨论】:

    【解决方案6】:

    requests python 库的核心开发团队的某个人已经记录了 将更改限制在一台或几台服务器上的方法:

    https://lukasa.co.uk/2017/02/Configuring_TLS_With_Requests/

    如果您的代码与多台服务器交互,最好不要因为一台服务器的配置有问题而降低所有连接的安全要求。

    该代码开箱即用。 也就是使用我自己的值为CIPHERS'ALL:@SECLEVEL=1'

    【讨论】:

      【解决方案7】:

      在 CentOS 7 上,在 /etc/pki/tls/openssl.cnf 中搜索以下内容:

      [ crypto_policy ]
      .include /etc/crypto-policies/back-ends/opensslcnf.config  
      [ new_oids ]  
      

      在 /etc/crypto-policies/back-ends/opensslcnf.config 中设置“ALL:@SECLEVEL=1”。

      【讨论】:

        【解决方案8】:

        我在从 18.04 升级到 Ubuntu 20.04 后遇到这个问题,以下命令对我有用。

        pip install --ignore-installed pyOpenSSL --upgrade
        

        【讨论】:

        • 我有 Ubuntu 20.04 但它对我没有帮助。
        【解决方案9】:

        在 docker 镜像中,您可以在 Dockerfile 中添加以下命令来解决此问题:

        RUN sed -i '/CipherString = DEFAULT/s/^#\?/#/' /etc/ssl/openssl.cnf
        

        这会自动消除有问题的CipherString 行。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-25
          • 1970-01-01
          • 2018-02-21
          • 1970-01-01
          相关资源
          最近更新 更多