Python Requests 获取 SSL 错误

24

尝试使用Requests session进行简单的get请求,但我一直在特定站点上遇到SSL错误。我认为问题可能出在该站点上(我使用https://www.ssllabs.com进行了扫描,结果如下),但我不能确定,因为我在这个领域没有知识 :) 我肯定想知道发生了什么。

解决方案/说明将是很好的, 谢谢!

代码:

import requests

requests.get('https://www.reporo.com/')

我遇到了以下错误:

SSLError: [Errno bad handshake] [('SSL routines', 'SSL3_GET_SERVER_CERTIFICATE', 'certificate verify failed')]

---------------------------------------------------------------------------
SSLError                                  Traceback (most recent call last)
<ipython-input-7-cfc21b287fee> in <module>()
----> 1 requests.get('https://www.reporo.com/')

/usr/local/lib/python2.7/dist-packages/requests/api.pyc in get(url, **kwargs)
     63 
     64     kwargs.setdefault('allow_redirects', True)
---> 65     return request('get', url, **kwargs)
     66 
     67 

/usr/local/lib/python2.7/dist-packages/requests/api.pyc in request(method, url, **kwargs)
     47 
     48     session = sessions.Session()
---> 49     response = session.request(method=method, url=url, **kwargs)
     50     # By explicitly closing the session, we avoid leaving sockets open which
     51     # can trigger a ResourceWarning in some cases, and look like a memory leak

/usr/local/lib/python2.7/dist-packages/requests/sessions.pyc in request(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)
    459         }
    460         send_kwargs.update(settings)
--> 461         resp = self.send(prep, **send_kwargs)
    462 
    463         return resp

/usr/local/lib/python2.7/dist-packages/requests/sessions.pyc in send(self, request, **kwargs)
    571 
    572         # Send the request
--> 573         r = adapter.send(request, **kwargs)
    574 
    575         # Total elapsed time of the request (approximately)

/usr/local/lib/python2.7/dist-packages/requests/adapters.pyc in send(self, request, stream, timeout, verify, cert, proxies)
    429         except (_SSLError, _HTTPError) as e:
    430             if isinstance(e, _SSLError):
--> 431                 raise SSLError(e, request=request)
    432             elif isinstance(e, ReadTimeoutError):
    433                 raise ReadTimeout(e, request=request)

SSLError: [Errno bad handshake] [('SSL routines', 'SSL3_GET_SERVER_CERTIFICATE', 'certificate verify failed')]

我在 https://www.ssllabs.com 进行了一次扫描,得到了以下结果:

SSL Report: reporo.com
Assessed on:  Sun Feb 22 21:42:57 PST 2015 | Clear cache Scan Another >>

    Server  Domain(s)   Test time   Grade
1   154.51.128.13 
Certificate not valid for domain name 
reporo.com
Sun Feb 22 21:40:53 PST 2015 
Duration: 9.167 sec -
2   198.12.15.168 
protected.ddosdefend.com 
Ready 
www.reporo.com

Sun Feb 22 21:41:02 PST 2015 
Duration: 115.189 sec   
F

1
服务器使用了无效的证书,就像报告所说的那样。因此,requests 拒绝连接,因为信息传输可能不安全。 - MattDMo
有没有我可以使用的解决方法? - Captain_Meow_Meow
6个回答

25

www.reporo.com的证书本身是有效的(不是reporo.com),但根据ssllabs的报告,它缺少链式证书:

Chain issues    Incomplete
....
2   Extra download  Thawte DV SSL CA 
Fingerprint: 3ca958f3e7d6837e1c1acf8b0f6a2e6d487d6762 

"不完整"和"额外下载"是主要问题。一些浏览器可能已经缓存了丢失的链证书,其他浏览器将会下载,还有一些将会失败。如果你在一个没有任何证书缓存的新的Firefox配置文件中尝试该网站,它也会失败。

您可以下载缺失的链证书,并将其作为可信CA证书与请求的verify参数一起使用。不要只是禁用验证,因为那样就容易受到中间人攻击。

按步骤说明:


2
你能详细说明如何下载和使用CA证书吗?(或者提供相关链接)这个主题对我来说很新,我想避免出错 :) - Captain_Meow_Meow
4
我已经将逐步说明添加到答案中。 - Steffen Ullrich
1
看起来他们已经改变了他们的网站。现在它使用默认的CA路径工作,也就是说你不需要添加特定的路径。显式路径不再起作用,因为当前证书需要另一个信任链。 - Steffen Ullrich
@SteffenUllrich,您能否详细说明一下您是如何找到缺失的链式证书的?我承认我自己无法弄清楚,也许我做错了什么……我不知道。 - Albert
1
@Albert:自2015年以来,他们已经改变了界面。现在你必须点击“点击此处展开”才能获取证书路径的详细信息和缺少哪些证书(即额外下载)。然后,你可以通过使用谷歌或类似工具搜索显示的指纹来找到这些证书并进行下载。 - Steffen Ullrich
显示剩余9条评论

17

您可以禁用证书验证:

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

但如果没有证书验证,就没有中间人攻击保护。


29
请不要建议禁用验证而不指出安全影响。 - Steffen Ullrich

2
遇到类似问题,按照以下步骤修复:
pip install -U requests[security]

1
Stackoverflow说不要留下像“谢谢”或“+1”的评论,但是你简单的指导结束了我的搜索,并且值得更多的关注。 - silian-rail

1

我遇到了相同的错误。将requests从requests-2.17.3降级到requests-2.11.0可以解决该问题。

pip uninstall requests
pip install requests==2.11.0

1
你有什么想法,这两个版本之间的哪些更改会导致握手失败? - jxramos

1

Steffen Ullrich提供了最佳答案,当网站由浏览器确定具有有效证书但完整的认证链未由服务器提供时,该怎么办。

有时仅缺少根或中间证书,而浏览器可以下载或验证缺失的证书,但是使用请求模块时,您需要手动验证它。我使用 SSL实验室确定完整的证书链作为.pem文件。输入感兴趣的网站的url,等待测试完成。然后导航到并展开“认证路径”。可能会有多个受信任的路径(或没有,在这种情况下,最终请求将无法成功),在右侧您将看到一个下载链按钮。

将完整链作为文本复制并保存为.pem文件。然后,在进行请求时,将此.pem文件的路径传递给请求函数:

r = requests.get(url, verify= "path/to/chain.pem")

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接