Python的HTTPS验证

简述

最近在研究微信机器人的Python脚本,很是好奇它是如何做到的,后来发现它只是模拟登陆网页版微信而实现的,通过研究这些东西也学到了不少东西,首先是脚本的里面都是通过简单的HTTPS请求来实现的,既然是模拟网页版微信,那一定是抓取网页版微信的接口,以前一直以为HTTPS是不可抓取的,后来发现并非如此,通过《使用 Charles 获取 https 的数据》一步步抓取了网站的接口,发现腾讯的安全措施也并非那么好,经过解析都是明文的,瞬间感觉很不安全。那么HTTPS真的是这么不安全吗,也不是,只不过是没有使用SSL Pinning,如果是使用了SSL Pinning的HTTPS还是无法抓取的。

起因

了解了微信机器人的原理,我就慢慢摸索着这个项目的具体实现细节,我想直接使用Charles抓取这个项目究竟使用了哪些微信的接口,发现没有成功,给我抛了个下面的错误:

requests.exceptions.SSLError: ("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",)

猜想

我很是纳闷,测试了自己的Charles抓取HTTPS也是正常的,那Charles的代理证书就没问题,为什么Python还给抛出了这样的一个错误,之后我将Charles关闭,项目正常运行,很是奇怪,我猜想肯定代理的问题,毕竟Charles抓取的原理是作为一个代理来截获的。

调试

于是我尝试一步步调试,发现Python确实能识别出代理和真实请求,于是我使用了下面去掉代理的代码测试了一下:

r = requests.get(url='https://login.weixin.qq.com',proxies={'no_proxy':'login.weixin.qq.com'})

结果是成功的发送了请求,也没有报错,天真的我以为可以确定就是代理的问题,其实也不尽然,这个请求Charles居然抓不到了,欲哭无泪,只好继续一步步调试到底是哪里出了问题。
结果我发现了Python的如果发现是HTTPS请求,会默认开启证书验证verify = True,但是我的Charles代理证书也是没问题的,为什么就通不过呢,真实的原因就在于,Python对于HTTPS请求中的证书的验证,并不会去系统的证书系统(Mac的钥匙串访问)验证,而是使用它自己的证书cacert.pem,被Python放置和定义的路径为DEFAULT_CA_BUNDLE_PATH = certs.where(),其值是/Library/Python/2.7/site-packages/certifi/cacert.pem,我们双击打开之后会发现他会安装在了钥匙串访问中,

Python的HTTPS验证_第1张图片
cacert.pem

其实在系统的根证书的列表里也有以 GlobalSign Root CA命名的证书,但其实两个是不一样的东西,我们可以用文本方式打开Python下的 cacert.pem证书,发现里面有很多证书颁发机构
Python的HTTPS验证_第2张图片
cacert.pem中的GeoTrust Global

也就是说如果你的HTTPS请求到的服务器证书的颁发机构被包含在Python的证书下,验证就能通过,网页版的微信的HTTPS服务器证书:
Python的HTTPS验证_第3张图片
web微信的证书

包含在 cacert.pem证书里面,那自然是可以通过请求,而Charles的自签名证书,是没有在 cacert.pem证书里,验证就不会通过。

验证

你还记得你买票的时候,12306会提示你安装一个证书吗,毕竟这不是一个在CA付费机构购买且让浏览器信任的证书,这是一个自签名证书的网站,我们可以通过12306网站证明一下证书的问题:

r = requests.get(url='https://kyfw.12306.cn/otn/')

正如预想的那样,Python抛出了certificate verify failed的错误。

参考

  • urllib and “SSL: CERTIFICATE_VERIFY_FAILED” Error

你可能感兴趣的:(Python的HTTPS验证)