Python - requests.exceptions.SSLError - dh键太小

时间:2016-06-24 14:13:54

标签: python ssl python-requests

我正在使用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)

8 个答案:

答案 0 :(得分:8)

我有同样的问题。

它通过评论得以解决

CipherString = DEFAULT@SECLEVEL=2

/etc/ssl/openssl.cnf中的行。

答案 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)')))

答案 2 :(得分:4)

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

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

使用您自己的密码集请求很棘手。有关示例,请参阅Why does Python requests ignore the verify parameter?

答案 3 :(得分:4)

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

这不仅是服务器端的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.DEFAULT_SSL_CIPHER_LIST += 'HIGH:!DH:!aNULL'
except AttributeError:
    # no pyopenssl support used / needed / available
    pass

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

答案 4 :(得分:2)

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

pip install --ignore-installed pyOpenSSL --upgrade

答案 5 :(得分:1)

在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”。

答案 6 :(得分:0)

我将在此处打包我的解决方案。我不得不修改python SSL库,这是可能的,因为我正在docker容器中运行代码,但这可能是您不想要的。

  1. 获取服务器支持的密码。在我的情况下是第三方电子邮件服务器,我使用描述为enter link description here
  2. 的脚本

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. 强制输入您的代码中的错误:

    回溯(最近通话最近):   在第52行的文件“ my_custom_script.py”中     imap = IMAP4_SSL(imap_host)    init 中的文件“ /usr/lib/python2.7/imaplib.py”,行1169     IMAP4。初始化(自身,主机,端口)    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'
    )

答案 7 :(得分:0)

来自python库的核心开发团队的人已记录在案 将更改限制为一台或几台服务器的方法:

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

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

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