簡述
最近在研究微信機器人的Python腳本,很是好奇它是如何做到的,后來發(fā)現(xiàn)它只是模擬登陸網(wǎng)頁版微信而實現(xiàn)的,通過研究這些東西也學到了不少東西,首先是腳本的里面都是通過簡單的HTTPS請求來實現(xiàn)的,既然是模擬網(wǎng)頁版微信,那一定是抓取網(wǎng)頁版微信的接口,以前一直以為HTTPS是不可抓取的,后來發(fā)現(xiàn)并非如此,通過《使用 Charles 獲取 https 的數(shù)據(jù)》一步步抓取了網(wǎng)站的接口,發(fā)現(xiàn)騰訊的安全措施也并非那么好,經(jīng)過解析都是明文的,瞬間感覺很不安全。那么HTTPS真的是這么不安全嗎,也不是,只不過是沒有使用SSL Pinning,如果是使用了SSL Pinning的HTTPS還是無法抓取的。
起因
了解了微信機器人的原理,我就慢慢摸索著這個項目的具體實現(xiàn)細節(jié),我想直接使用Charles抓取這個項目究竟使用了哪些微信的接口,發(fā)現(xiàn)沒有成功,給我拋了個下面的錯誤:
requests.exceptions.SSLError: ("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",)
猜想
我很是納悶,測試了自己的Charles抓取HTTPS也是正常的,那Charles的代理證書就沒問題,為什么Python還給拋出了這樣的一個錯誤,之后我將Charles關閉,項目正常運行,很是奇怪,我猜想肯定代理的問題,畢竟Charles抓取的原理是作為一個代理來截獲的。
調(diào)試
于是我嘗試一步步調(diào)試,發(fā)現(xiàn)Python確實能識別出代理和真實請求,于是我使用了下面去掉代理的代碼測試了一下:
r = requests.get(url='https://login.weixin.qq.com',proxies={'no_proxy':'login.weixin.qq.com'})
結(jié)果是成功的發(fā)送了請求,也沒有報錯,天真的我以為可以確定就是代理的問題,其實也不盡然,這個請求Charles居然抓不到了,欲哭無淚,只好繼續(xù)一步步調(diào)試到底是哪里出了問題。
結(jié)果我發(fā)現(xiàn)了Python的如果發(fā)現(xiàn)是HTTPS請求,會默認開啟證書驗證verify = True,但是我的Charles代理證書也是沒問題的,為什么就通不過呢,真實的原因就在于,Python對于HTTPS請求中的證書的驗證,并不會去系統(tǒng)的證書系統(tǒng)(Mac的鑰匙串訪問)驗證,而是使用它自己的證書cacert.pem,被Python放置和定義的路徑為DEFAULT_CA_BUNDLE_PATH = certs.where(),其值是/Library/Python/2.7/site-packages/certifi/cacert.pem,我們雙擊打開之后會發(fā)現(xiàn)他會安裝在了鑰匙串訪問中,

其實在系統(tǒng)的根證書的列表里也有以
GlobalSign Root CA命名的證書,但其實兩個是不一樣的東西,我們可以用文本方式打開Python下的cacert.pem證書,發(fā)現(xiàn)里面有很多證書頒發(fā)機構(gòu)
也就是說如果你的HTTPS請求到的服務器證書的頒發(fā)機構(gòu)被包含在Python的證書下,驗證就能通過,網(wǎng)頁版的微信的HTTPS服務器證書:

包含在
cacert.pem證書里面,那自然是可以通過請求,而Charles的自簽名證書,是沒有在cacert.pem證書里,驗證就不會通過。
驗證
你還記得你買票的時候,12306會提示你安裝一個證書嗎,畢竟這不是一個在CA付費機構(gòu)購買且讓瀏覽器信任的證書,這是一個自簽名證書的網(wǎng)站,我們可以通過12306網(wǎng)站證明一下證書的問題:
r = requests.get(url='https://kyfw.12306.cn/otn/')
正如預想的那樣,Python拋出了certificate verify failed的錯誤。
參考