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

2025-03-11 08:50:00
admin
原创
72
摘要:问题描述:我正在使用 Python 和请求抓取一些内部页面。我已关闭 SSL 验证和警告。requests.packages.urllib3.disable_warnings() page = requests.get(url, verify=False) 在某些服务器上,我收到了无法解决的 SSL 错误。T...

问题描述:

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

在 Windows 和 OSX 中,Cygwin 内外都会发生这种情况。我的研究表明服务器上的 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)

解决方案 1:

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

这不仅是服务器端的 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)

解决方案 2:

这对我也有用:

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

解决方案 3:

禁用警告或证书验证不会有帮助。根本问题是服务器使用的 DH 密钥较弱,可能会在Logjam Attack中被滥用。

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

使用带有您自己的密码集的请求比较棘手。请参阅为什么 Python 请求会忽略验证参数?以获取示例。

解决方案 4:

我遇到了同样的问题。

并且已通过评论修复

CipherString = DEFAULT@SECLEVEL=2

插线/etc/ssl/openssl.cnf

解决方案 5:

urllib3从 的版本 2或 的版本 2.30开始requests,该DEFAULT_CIPHERS属性被删除,并且这里提出的大多数解决方案不再有效。相反,您必须创建一个SSLContext

from urllib3.util import create_urllib3_context
from urllib3 import PoolManager

ctx = create_urllib3_context(ciphers=":HIGH:!DH:!aNULL")
http = PoolManager(ssl_context=ctx)
http.request("GET", ...)

如果你正在使用,requests则必须将自己的子类化HTTPAdapter,以初始化 a PoolManager,并将其挂载到 a 中Session

from urllib3.util import create_urllib3_context
from urllib3 import PoolManager
from requests.adapters import HTTPAdapter
from requests import Session

class AddedCipherAdapter(HTTPAdapter):
  def init_poolmanager(self, connections, maxsize, block=False):
    ctx = create_urllib3_context(ciphers=":HIGH:!DH:!aNULL")
    self.poolmanager = PoolManager(
      num_pools=connections,
      maxsize=maxsize,
      block=block,
      ssl_context=ctx
    )

s = Session()
s.mount("https://example.org", AddedCipherAdapter())
s.get("https://example.org/path")

这种方法的好处是其范围仅限于受影响的服务器https://example.org

此外,有些人希望禁用主机名和证书检查,我不建议这样做,而且这不是修复弱 DH 密钥所必需的。这些人必须在创建的上下文对象上添加这些选项ctx

ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

再次强调,请在执行禁用证书检查之前了解所涉及的安全风险。

解决方案 6:

来自请求 Python 库核心开发团队的某个人记录了一个方法,用于将更改限制在一台或几台服务器上:

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

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

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

解决方案 7:

不要覆盖默认的全局密码可能会更安全,而是在特定会话中使用所需的密码创建自定义 HTTPAdapter:

import ssl
from typing import Any

import requests

class ContextAdapter(requests.adapters.HTTPAdapter):
    """Allows to override the default context."""

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        self.ssl_context: ssl.SSLContext|None = kwargs.pop("ssl_context", None)

        super().__init__(*args, **kwargs)

    def init_poolmanager(self, *args: Any, **kwargs: Any) -> Any:
        # See available keys in urllib3.poolmanager.SSL_KEYWORDS
        kwargs.setdefault("ssl_context", self.ssl_context)

        return super().init_poolmanager(*args, **kwargs)

然后您需要创建自定义上下文,例如:

import ssl

def create_context(
    ciphers: str, minimum_version: int, verify: bool
) -> ssl.SSLContext:
    """See https://peps.python.org/pep-0543/."""
    ctx = ssl.create_default_context()

    # Allow to use untrusted certificates.
    if not verify:
        ctx.check_hostname = False
        ctx.verify_mode = ssl.CERT_NONE

    # Just for example.
    if minimum_version == ssl.TLSVersion.TLSv1:
        ctx.options &= (
            ~getattr(ssl, "OP_NO_TLSv1_3", 0)
            & ~ssl.OP_NO_TLSv1_2
            & ~ssl.OP_NO_TLSv1_1
        )
        ctx.minimum_version = minimum_version

    ctx.set_ciphers(ciphers)

    return ctx

然后您需要使用自定义上下文规则配置每个网站:

session = requests.Session()
session.mount(
    "https://dh.affected-website.com",
    ContextAdapter(
        ssl_context=create_context(
            ciphers="HIGH:!DH:!aNULL"
        ),
    ),
)
session.mount(
    "https://only-elliptic.modern-website.com",
    ContextAdapter(
        ssl_context=create_context(
            ciphers="ECDHE+AESGCM"
        ),
    ),
)
session.mount(
    "https://only-tls-v1.old-website.com",
    ContextAdapter(
        ssl_context=create_context(
            ciphers="DEFAULT:@SECLEVEL=1",
            minimum_version=ssl.TLSVersion.TLSv1,
        ),
    ),
)

result = session.get("https://only-tls-v1.old-website.com/object")

阅读完所有答案后,我可以说@bgoeman 的答案与我的很接近,您可以点击他们的链接了解更多信息。

解决方案 8:

根据用户 bgoeman 给出的回答,以下代码可以工作,它保留默认密码,仅添加安全级别。

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

解决方案 9:

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

pip install --ignore-installed pyOpenSSL --upgrade

解决方案 10:

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

  1. 获取服务器支持的密码。我的情况是第三方电子邮件服务器,我使用了脚本描述的SSL/TLS 密码套件列表

检查支持的密码

#!/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 YES

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

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

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

  1. 强制代码中的错误:

回溯(最近一次调用):文件“my_custom_script.py”,第 52 行,在 imap = IMAP4_SSL(imap_host)文件“/usr/lib/python2.7/imaplib.py”,第 1169 行,在init
IMAP4 中。init(self,host,port)文件“/usr/lib/python2.7/imaplib.py”,第 174 行,在init
self.open(host,port)文件“/usr/lib/python2.7/imaplib.py”,第 1181 行,在 open 中 self.slobj = ssl.wrap_socket(self.sock,self.keyfile,self.certfile)文件“/usr/lib/python2.7/ssl.py”,第 931 行,在 wrap_socket 中 ciphers=ciphers)文件“/usr/lib/python2.7/ssl.py”,第 599 行,在init
self.do_handshake() 文件“/usr/lib/python2.7/ssl.py”,第 828 行,在 do_handshake 中 self._sslobj.do_handshake() ssl.SSLError:[SSL:DH_KEY_TOO_SMALL] dh 密钥太小(_ssl.c:727)

  1. 获取使用的python SSL库路径,在本例中为:

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

  1. 編輯:

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

解决方案 11:

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

解决方案 12:

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

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

这会自动注释掉有问题的CipherString行。

解决方案 13:

如果您正在使用httpx库,则可以跳过警告:

import httpx

httpx._config.DEFAULT_CIPHERS += ":HIGH:!DH:!aNULL"

解决方案 14:

我遇到了以下错误:

  • SSLError:[SSL:DH_KEY_TOO_SMALL] dh 密钥太小(_ssl.c:727)

我解决了它(Fedora):

  • python2.7 -m pip 卸载请求

  • python2.7 -m pip 卸载 pyopenssl

  • python2.7 -m pip install pyopenssl==你的版本

  • python2.7 -m pip install 请求==你的版本

顺序模块安装导致:

  • 请求.包.urllib3.contrib.pyopenssl.util.ssl_.DEFAULT_CIPHERS

当模块确实存在时,“requests.packages.urllib3.contrib”中出现 AttributeError“pyopenssl”。

解决方案 15:

我专门为这个帖子创建了一个帐户,因为这些答案对我都不起作用。使用Robin De Schepper的一段代码作为基准,我成功地让它工作了,只需要一个小小的调整

原始代码片段:

from urllib3.util import create_urllib3_context
from urllib3 import PoolManager

ctx = create_urllib3_context(ciphers=":HIGH:!DH:!aNULL")
http = PoolManager(ssl_context=ctx)
http.request("GET", ...)

修改后的一段代码:

import ssl # needed for the cert argument
from urllib3.util import create_urllib3_context
from urllib3 import PoolManager

# cert_reqs=ssl.CERT_NONE is the one adjustment that made this work
ctx = create_urllib3_context(ciphers=":HIGH:!DH:!aNULL", cert_reqs=ssl.CERT_NONE)
http = PoolManager(ssl_context=ctx)
http.request("GET", ...)

解决方案 16:

如果有人在使用 ftplib 时遇到同样的问题,我会添加此内容。

解决方案是使用旧配置创建上下文:

context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.set_ciphers('DEFAULT@SECLEVEL=1')

with ftplib.FTP_TLS(
    host=self.ftp_host,
    user=self.ftp_user,
    passwd=self.ftp_password,
    context=context,
) as ftp:
    ...
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3983  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2747  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   82  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   90  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   79  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用