【问题标题】:Python Requests throwing SSLErrorPython 请求抛出 SSLError
【发布时间】:2016-02-05 06:30:52
【问题描述】:

我正在编写一个涉及 CAS、jspring 安全检查、重定向等的简单脚本。我想使用 Kenneth Reitz 的 python 请求,因为它是一项很棒的工作!但是,CAS 需要通过 SSL 进行验证,所以我必须先通过这一步。我不知道 Python 请求想要什么?这个 SSL 证书应该放在哪里?

Traceback (most recent call last):
  File "./test.py", line 24, in <module>
  response = requests.get(url1, headers=headers)
  File "build/bdist.linux-x86_64/egg/requests/api.py", line 52, in get
  File "build/bdist.linux-x86_64/egg/requests/api.py", line 40, in request
  File "build/bdist.linux-x86_64/egg/requests/sessions.py", line 209, in request 
  File "build/bdist.linux-x86_64/egg/requests/models.py", line 624, in send
  File "build/bdist.linux-x86_64/egg/requests/models.py", line 300, in _build_response
  File "build/bdist.linux-x86_64/egg/requests/models.py", line 611, in send
requests.exceptions.SSLError: [Errno 1] _ssl.c:503: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

【问题讨论】:

  • 你能分享更多的代码信息吗?好像少了一个步骤。
  • 您应该始终提及您需要帮助的软件版本。
  • 我在使用 python 3.5 tornado 4.4 时遇到了这个问题。 HTTPRequest设置了validate_cert=True,所以你可以设置为False来处理它
  • 试试这个:requests.get('example.com', verify=certifi.where())

标签: python ssl python-requests urllib3


【解决方案1】:

来自请求documentation on SSL verification

Requests 可以为 HTTPS 请求验证 SSL 证书,就像 Web 浏览器一样。要检查主机的 SSL 证书,您可以使用 verify 参数:

>>> requests.get('https://kennethreitz.com', verify=True)

如果您不想验证您的 SSL 证书,请使用verify=False

【讨论】:

  • 好吧,我添加了 verify=True,但仍然收到完全相同的错误。不用找了。必须有其他东西,但不知道它可能是什么。
  • 我想我现在陷入了 SSL 疯狂。我将此添加到我的初始 get...get(url1, headers=headers, cert='/etc/pki/tls/cert.pem', verify=True, config=my_config) 所以,现在我收到了这个错误。 requests.exceptions.SSLError: [Errno 336265225] _ssl.c:351: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib 我不知道这意味着什么。
  • 如果您不想验证证书,只需设置 verify=False,如果您有自签名证书,请设置 iow
  • 如果您有自签名证书,请下载它并将验证设置为其文件名。设置 verify=False 没有任何借口。 verify='/path/to/cert.pem'
  • 抱歉,我需要否决这个答案,因为请求不能“像 Web 浏览器一样”处理 HTTPS 请求。如果未在服务器上声明完整的 SSL 信任链(包括中间证书)并且需要额外下载证书,您将收到上述 SSL 验证错误。 Web 浏览器将执行额外的下载,并且不会标记任何证书错误。这是 Web 浏览器和请求不同的一种方式。还有其他的。 Requests 做了一些验证,但不如浏览器。
【解决方案2】:

您遇到的问题是由不受信任的 SSL 证书引起的。

就像之前评论中提到的@dirk,最快的修复方法是设置verify=False

requests.get('https://example.com', verify=False)

请注意,这将导致证书无法验证。 这将使您的应用程序面临安全风险,例如中间人攻击。

当然,应用判断。正如 cmets 中所述,这可能对于快速/一次性应用程序/脚本是可以接受的,但实际上不应该用于生产软件

如果只是跳过证书检查在您的特定上下文中是不可接受的,请考虑以下选项,您最好的选择是将verify 参数设置为证书的.pem 文件路径的字符串(您应该通过某种安全方式获得)。

因此,从 2.0 版开始,verify 参数接受以下值及其各自的语义:

  • True:使证书根据库自己的受信任证书颁发机构进行验证(注意:您可以通过 Certifi 库查看哪些根证书请求使用,这是一个从请求中提取的 RC 的信任数据库:Certifi - Trust Database for Humans)。
  • False:绕过证书验证完全
  • 请求用于验证证书的 CA_BUNDLE 文件的路径。

来源:Requests - SSL Cert Verification

还可以查看同一链接上的cert 参数。

【讨论】:

  • 是的,当我在 ubuntu 中使用 dotCloud 时,出现了相同的“证书验证失败”。在“/usr/local/lib/python2.6/dist-packages/dotcloud/client/client.py”中修改“requests.session(headers=headers, hooks=hooks, verify=False)”后,它工作了。
  • 这未标记为正确,但我可以验证它是否有效(与下面的答案相反)。
  • @khalid13:斧头“有效”作为头痛药(没有头 - 没有头痛)。这并不意味着以这种方式使用它是一个好主意。 verify=False 禁用主机的 SSL 证书检查。
  • @J.F.Sebastian 老实说,这取决于你在做什么。对于我的快速/一次性应用程序来说,这已经绰绰有余了。
  • @diyism 做出这样的改变听起来非常不安全……
【解决方案3】:

你要使用的CA文件名可以通过verify传递:

cafile = 'cacert.pem' # http://curl.haxx.se/ca/cacert.pem
r = requests.get(url, verify=cafile)

如果您使用verify=True,则requests 使用其自己的 CA 集,该 CA 集可能没有签署您的服务器证书的 CA。

【讨论】:

  • @9emE0iL18gxCqLT:为什么您认为所有系统都使用您提供的路径? requests 可以打包为您的发行版。运行python -mrequests.certs 找出它指向的位置。
  • 如果 Python 请求的 cacert 包已过期,我该如何更新它?
  • 你不应该使用 curl 中的 cacert.pem。它包含许多已撤销的证书。查看证书(Requests 使用):certifi.io
  • @KennethReitz: 1- Requests 对 OP 使用的内容失败(否则就不存在问题)2- cacert.pem is CA certificates extracted from Mozilla (by cURL) -- 这只是一个示例(如果一个流行的 CA 列表使用web-browser 不能用作示例,那么我不知道可以是什么) - 如果默认列表失败,您可以传递您自己的 CA 文件。
  • 您可以这样做并同时使用客户端证书吗?我遇到了这个问题。
【解决方案4】:

我找到了解决类似问题的特定方法。这个想法是指向存储在system 并由另一个基于 ssl 的应用程序使用的 cacert 文件。

在 Debian 中(我不确定在其他发行版中是否相同)证书文件 (.pem) 存储在 /etc/ssl/certs/ 所以,这是适合我的代码:

import requests
verify='/etc/ssl/certs/cacert.org.pem'
response = requests.get('https://lists.cacert.org', verify=verify)

为了猜测 pem 文件选择什么,我浏览到了 url 并检查了哪个证书颁发机构 (CA) 生成了证书。

编辑:如果您无法编辑代码(因为您正在运行第三个应用程序),您可以尝试将pem 证书直接添加到/usr/local/lib/python2.7/dist-packages/requests/cacert.pem 中(例如将其复制到文件末尾)。

【讨论】:

  • Related post 用于调试python使用的CA_BUNDLE。
  • 用操作系统商店的符号链接替换/usr/local/lib/python2.7/dist-packages/requests/cacert.pem 怎么样?
【解决方案5】:

我遇到了同样的问题。原来我没有在我的服务器上安装中间证书(只需将其附加到证书的底部,如下所示)。

https://www.digicert.com/ssl-support/pem-ssl-creation.htm

确保您已安装 ca-certificates 软件包:

sudo apt-get install ca-certificates

更新时间也可以解决这个问题:

sudo apt-get install ntpdate
sudo ntpdate -u ntp.ubuntu.com

如果您使用的是自签名证书,您可能需要手动将其添加到您的系统中。

【讨论】:

  • 注意,这仅适用于通过 apt-get 进行的 Requests 安装,它被 Debian/Ubuntu 修改为使用系统证书。请求使用自己精心策划的 CA 捆绑包进行适当的运送:certifi.io
  • 根CA难道不够吗?为什么需要中间体?
【解决方案6】:

如果您不关心证书,请使用verify=False

import requests

url = "Write your url here"

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

【讨论】:

    【解决方案7】:

    如果从另一个包调用请求,则添加选项是不可行的。在这种情况下,将证书添加到 cacert 捆绑包是直接路径,例如我必须添加“StartCom Class 1 Primary Intermediate Server CA”,为此我将根证书下载到 StartComClass1.pem。鉴于我的 virtualenv 名为 caldav,我添加了以下证书:

    cat StartComClass1.pem >> .virtualenvs/caldav/lib/python2.7/site-packages/pip/_vendor/requests/cacert.pem
    cat temp/StartComClass1.pem >> .virtualenvs/caldav/lib/python2.7/site-packages/requests/cacert.pem
    

    其中一个可能就足够了,我没有检查

    【讨论】:

      【解决方案8】:

      如果要删除警告,请使用以下代码。

      import urllib3
      
      urllib3.disable_warnings()
      

      verify=Falserequest.getpost 方法

      【讨论】:

        【解决方案9】:

        我在使用 aws boto3 时遇到了同样的问题和 ssl certificate verify failed 问题,通过查看 boto3 代码,我发现 REQUESTS_CA_BUNDLE 没有设置,所以我通过手动设置修复了这两个问题:

        from boto3.session import Session
        import os
        
        # debian
        os.environ['REQUESTS_CA_BUNDLE'] = os.path.join(
            '/etc/ssl/certs/',
            'ca-certificates.crt')
        # centos
        #   'ca-bundle.crt')
        

        对于 aws-cli,我想在 ~/.bashrc 中设置 REQUESTS_CA_BUNDLE 将解决此问题(未测试,因为我的 aws-cli 没有它也可以工作)。

        REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt # ca-bundle.crt
        export REQUESTS_CA_BUNDLE
        

        【讨论】:

        • 这解决了我的问题!我在 Mac 上使用 Charles Proxy 来调试一个对 HTTPS API 进行 JSON 调用的库。我按照指定安装了 Charless 证书,将其添加到钥匙串中,但 Python 一直失败: SSLError: ("bad handshake: Error([('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')],)" ,) 为了解决这个问题,我最终听从了您关于添加 REQUESTS_CA_BUNDLE 并将 Charles 证书从我的钥匙串中导出为 .pem 文件的建议。现在,它起作用了!
        • 谢谢,打开的 Fiddler 也有同样的问题
        • @user565447 我现在正试图让这个与 Fiddler 一起工作。应该将 REQUESTS_CA_BUNDLE 设置为 Fiddler 的证书吗?
        【解决方案10】:

        我在使用 gspread 时遇到了同样的问题,这些命令对我有用:

        sudo pip uninstall -y certifi
        sudo pip install certifi==2015.04.28
        

        【讨论】:

        • 这是为我做的。谢谢:)
        • 这有从旧版本的 certifi 重新安装可能被撤销/不受信任的证书的缺点,不推荐。
        • 如果由于某种原因你不得不坚持使用早期版本的 python 2.7,降级 certifi 是唯一对我有用的方法
        【解决方案11】:

        如果您有一个依赖于 requests 的库并且您无法修改验证路径(例如使用 pyvmomi),那么您必须找到与请求捆绑在一起的 cacert.pem 并将您的 CA 附加到那里。这是查找cacert.pem 位置的通用方法:

        窗口

        C:\>python -c "import requests; print requests.certs.where()"
        c:\Python27\lib\site-packages\requests-2.8.1-py2.7.egg\requests\cacert.pem
        

        Linux

        #  (py2.7.5,requests 2.7.0, verify not enforced)
        root@host:~/# python -c "import requests; print requests.certs.where()"
        /usr/lib/python2.7/dist-packages/certifi/cacert.pem
        
        #  (py2.7.10, verify enforced)
        root@host:~/# python -c "import requests; print requests.certs.where()"
        /usr/local/lib/python2.7/dist-packages/requests/cacert.pem
        

        顺便说一句。 @requests-devs,将您自己的 cacerts 与 request 捆绑在一起真的很烦人……尤其是您似乎没有首先使用系统 ca 存储,而且这在任何地方都没有记录。

        更新

        在您使用库且无法控制 ca-bundle 位置的情况下,您也可以将 ca-bundle 位置显式设置为主机范围的 ca-bundle:

        REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-bundle.crt python -c "import requests; requests.get('https://somesite.com')";
        

        【讨论】:

        • 一百倍:key无法修改verify路径。
        • 如果您使用的是自签名证书怎么办?在这种情况下,CA 会是什么?
        • 小更新 - 对于 python 3.6,打印命令应该有括号 - python -c "import requests; print(requests.certs.where())"
        【解决方案12】:

        我为这个问题解决了几个小时。

        我尝试更新请求。然后我更新了证书。我将 verify 指向 certifi.where() (无论如何,代码默认情况下都会这样做)。没有任何效果。

        最后,我将我的 python 版本更新为 python 2.7.11。我使用的是 Python 2.7.5,它与验证证书的方式有些不兼容。一旦我更新了 Python(和一些其他依赖项),它就开始工作了。

        【讨论】:

        • 如果您将 OpenSSL 更新到 > 1.0.1 的版本,那么这可能就是问题所在。请参阅下面的答案。 stackoverflow.com/a/44543047/1413201
        • 从 Python 2.7.9 升级到 2.7.10 为我解决了这个问题。
        【解决方案13】:

        $ pip install -U requests[security]

        • 在 Python 2.7.6 @ Ubuntu 14.04.4 LTS 上测试
        • 在 Python 2.7.5 @ MacOSX 10.9.5(小牛队)上测试

        当这个问题被打开时(2012-05),请求版本是 0.13.1。在版本2.4.1 (2014-09) 中引入了“安全”附加功能,如果可用,使用certifi 包。

        目前(2016-09)主要版本是 2.11.1,效果很好没有 verify=False。无需使用requests.get(url, verify=False),如果安装了requests[security] extras。

        【讨论】:

        • 修复了两次pip install -U requests[security] --no-cachepip install certifi==2015.04.28
        • @alanjds 如果我想将 python 配置为信任某些 ssl 证书或禁用证书验证但在环境中全局禁用,而不编辑源代码怎么办?例如,如果我下载了现有的 Python 实用程序(例如 AWS CLI)并且我想信任证书或忽略这些工具的证书验证?
        • @Howiecamp 那么你可以通过 j-f-sebastian 回答,我猜:stackoverflow.com/a/12865159/798575
        • @alanjds 但是他的回答不是假设我正在编写代码和/或可以访问代码吗?我希望在环境级别实现这一点。
        • 在安装 requests 安全包之前做 pip install --upgrade pip 以避免其他错误
        【解决方案14】:

        请求模块中当前存在导致此错误的问题,存在于 v2.6.2 到 v2.12.4 (ATOW):https://github.com/kennethreitz/requests/issues/2573

        此问题的解决方法是添加以下行:requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS'

        【讨论】:

        • FWIW,它仍然存在于 requests==2.13.0 中。上述解决方法仍然可以修复它。
        【解决方案15】:

        正如@Rafael Almeida 所述,您遇到的问题是由不受信任的 SSL 证书引起的。就我而言,我的服务器不信任 SSL 证书。为了在不影响安全性的情况下解决这个问题,我downloaded the certificate 并将其安装在服务器上(只需双击 .crt 文件,然后安装证书...)。

        【讨论】:

          【解决方案16】:

          我遇到了类似或相同的认证验证问题。我读到 OpenSSL 版本低于 1.0.2,请求所依赖的版本有时无法验证强证书(请参阅here)。 CentOS 7 似乎使用 1.0.1e 似乎有问题。

          我不确定如何在 CentOS 上解决这个问题,所以我决定允许使用较弱的 1024 位 CA 证书。

          import certifi # This should be already installed as a dependency of 'requests'
          requests.get("https://example.com", verify=certifi.old_where())
          

          【讨论】:

          • 我使用的是 ArcGIS 安装的 Python 2.7.10,没有安装 certifi 模块。安装的 requests 模块是 2.11.1 版本。
          【解决方案17】:

          经过数小时的调试,我只能使用以下软件包来实现它:

          requests[security]==2.7.0  # not 2.18.1
          cryptography==1.9  # not 2.0
          

          使用OpenSSL 1.0.2g 1 Mar 2016

          如果没有这些软件包,verify=False 将无法正常工作。

          我希望这对某人有所帮助。

          【讨论】:

            【解决方案18】:

            如果请求调用隐藏在代码深处并且您不想安装服务器证书,那么,仅出于调试目的,可以对请求进行monkeypatch:

            import requests.api
            import warnings
            
            
            def requestspatch(method, url, **kwargs):
                kwargs['verify'] = False
                return _origcall(method, url, **kwargs)
            
            _origcall = requests.api.request
            requests.api.request = requestspatch
            warnings.warn('Patched requests: SSL verification disabled!')
            

            切勿在生产中使用!

            【讨论】:

              【解决方案19】:

              我不得不从 Python 3.4.0 升级到 3.4.6

              pyenv virtualenv 3.4.6 myvenv
              pyenv activate myvenv
              pip install -r requirements.txt
              

              【讨论】:

                【解决方案20】:

                我想聚会太晚了,但我想为像我这样的流浪者粘贴修复程序!因此,在 Python 3.7.x 上为我解决了以下问题

                在终端输入以下内容

                pip install --upgrade certifi      # hold your breath..
                

                再次尝试运行您的脚本/请求,看看它是否有效(我相信它还不会被修复!)。如果还是不行就直接在终端运行下面​​的命令

                open /Applications/Python\ 3.6/Install\ Certificates.command  # please replace 3.6 here with your suitable python version
                

                【讨论】:

                  【解决方案21】:

                  在我的情况下,原因是相当微不足道的。

                  我知道 SSL 验证直到几天前才有效,实际上是在另一台机器上工作。

                  我的下一步是比较正在执行验证的机器与未执行验证的机器之间的证书内容和大小。

                  这很快导致我确定“不正确”工作机器上的证书不好,一旦我用“好”证书替换它,一切都很好。

                  【讨论】:

                  • 是的,有时问题不在于代码,CERT 真的不匹配......(证书有时属于某个环境等......)
                  【解决方案22】:

                  这类似于@rafael-almeida 的回答,但我想指出的是,从请求 2.11+ 开始,verify 可以采用的值不是 3 个,实际上是 4 个:

                  • True:根据请求的内部可信 CA 进行验证。
                  • False:绕过证书验证完全。 (不推荐)
                  • CA_BUNDLE 文件的路径。请求将使用它来验证服务器的证书。
                  • 包含公共证书文件的目录的路径。请求将使用它来验证服务器的证书。

                  我剩下的答案是关于#4,如何使用包含证书的目录来验证:

                  获取所需的公共证书并将它们放在一个目录中。

                  严格来说,您可能“应该”使用带外方法获取证书,但您也可以使用任何浏览器下载它们。

                  如果服务器使用证书链,请务必获取链中的每一个证书。

                  根据请求文档,必须首先使用“rehash”实用程序 (openssl rehash) 处理包含证书的目录。

                  (这需要 openssl 1.1.1+,并且并非所有 Windows openssl 实现都支持 rehash。如果 openssl rehash 不适合您,您可以尝试在 https://github.com/ruby/openssl/blob/master/sample/c_rehash.rb 运行 rehash ruby​​ 脚本,尽管我没有试过这个。)

                  我在请求识别我的证书时遇到了一些麻烦,但在我使用 openssl x509 -outform PEM 命令将证书转换为 Base64 .pem 格式后,一切正常。

                  你也可以只做惰性重新散列:

                  try:
                      # As long as the certificates in the certs directory are in the OS's certificate store, `verify=True` is fine.
                      return requests.get(url, auth=auth, verify=True)
                  except requests.exceptions.SSLError:
                      subprocess.run(f"openssl rehash -compat -v my_certs_dir", shell=True, check=True)
                      return requests.get(url, auth=auth, verify="my_certs_dir")
                  

                  【讨论】:

                    【解决方案23】:

                    这只是您尝试解决问题的另一种方法。

                    如果您输入“www.example.com”,请求就会对您大喊大叫。如果输入“https://www.example.com”,则会出现此错误。因此,如果您不需要 https,则可以通过将“https”更改为“http”来避免错误。例如。 "http://www.example.com"

                    警告:不使用 HTTPS 通常不是一个好主意。见Why HTTPS for Everything?Why HTTPS matters

                    【讨论】:

                      【解决方案24】:

                      正如其他人所指出的,这个问题“是由不受信任的 SSL 证书引起的”。我的答案基于top-rated answerthis answer

                      您可以使用curl 测试证书:

                      curl -vvI https://example.com
                      

                      如果返回错误,您有 3 个选项:

                      1. 为了快速修复,您可以不验证证书:
                      requests.get('https://example.com', verify=False)
                      
                      1. 使用可信 CA 的证书传递 CA_BUNDLE 文件或目录的路径:
                      requests.get('https://example.com', verify='/path/to/certfile')
                      
                      1. 如果您有权访问,请修复 Web 服务器证书。

                      我的问题是因为我只使用了我网站的证书,而不是中间(又名链)证书。

                      如果您使用 Let's Encrypt,则应使用 fullchain.pem 文件,而不是 cert.pem

                      【讨论】:

                      • 感谢您的回答!在我的场景中,使用verify=False 的快速修复是唯一可能的解决方案。
                      【解决方案25】:

                      我找到了修复它的this answer

                      import ssl
                      import certifi
                      import urllib.request
                      
                      url = "https://www.google.com/"
                      html = urllib.request.urlopen(url, context=ssl.create_default_context(cafile=certifi.where()))
                      

                      不过,我不知道它的作用。

                      【讨论】:

                        【解决方案26】:

                        某些服务器没有 Letsencrypt 的受信任根证书。

                        requests.post(url, json=data)
                        

                        因此上述请求失败并显示 [SSL: CERTIFICATE_VERIFY_FAILED]

                        发生这种情况时,请从下面的链接下载有效的自签名“pem”证书。

                        https://letsencrypt.org/certificates/。 (Active ISRG Root X1 在撰写本文时)

                        现在,在验证参数中使用它,如下所示。

                        requests.post(url, json=data, verify='path-to/isrgrootx1.pem')
                        

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 2015-04-24
                          • 2021-05-29
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多