商務(wù)網(wǎng)站建設(shè)實(shí)驗(yàn)書網(wǎng)站片區(qū)
背景#
某天早上,正在一個(gè)會(huì)議時(shí),突然好幾個(gè)同事被叫出去了;后面才知道,是有業(yè)務(wù)同事反饋到領(lǐng)導(dǎo)那里,我們app里面某個(gè)功能異常。
具體是這樣,我們安卓版本的app是禁止截屏的(應(yīng)該是app里做了攔截),但部分頁面,支持配置成可以截屏。這個(gè)配置是通過后端接口獲取的,意思就是,如果調(diào)用這個(gè)接口失敗,就整個(gè)app默認(rèn)不能截屏;如果調(diào)用成功,就可以在配置的指定頁面截屏。
業(yè)務(wù)反饋就是說,之前可以截屏的幾個(gè)頁面,現(xiàn)在突然不能截屏了,不知道是不是我們搞了啥變更;后面產(chǎn)品去業(yè)務(wù)那深入了解了下,發(fā)現(xiàn):連接公司wifi后就不能截屏,用4g/5g是可以的。
排查過程#
前期排查#
安卓開發(fā)首先介入,具體方式就是,因?yàn)榭梢詮?fù)現(xiàn),找了個(gè)安卓設(shè)備,連接電腦就可以debug app(沒搞過安卓,具體不清楚),后面說是獲取截屏配置的接口(https)報(bào)錯(cuò)了:
ret: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found
丟出這個(gè)后,就沒有進(jìn)一步的動(dòng)作了,認(rèn)為不是安卓端的問題,因?yàn)橛?g就可以,只是wifi不行。然后問題就卡在那了。有人又丟出之前的一個(gè)變更通知,那次變更是這樣,之前我們https證書卸載都是在業(yè)務(wù)服務(wù)器的nginx做的,這樣的話,每個(gè)業(yè)務(wù)都會(huì)有自己的nginx,每個(gè)nginx都要負(fù)責(zé)https加解密,后來就提出來,要把這個(gè)https加解密前置,后面就前置到了負(fù)載均衡設(shè)備(比如典型的硬件負(fù)載均衡設(shè)備:F5)。
有人就說是不是動(dòng)了這個(gè)導(dǎo)致的,雖然這個(gè)極有可能,但是,沒有人去查,去確認(rèn)。
后端開始介入#
因?yàn)榘沧總?cè)認(rèn)為自己沒問題,產(chǎn)品后面來找我,我才開始介入這個(gè)問題。
下午先了解了下整個(gè)事情,比較重要的事情是,拿到了復(fù)現(xiàn)問題的手機(jī),然后試著連接電腦charles進(jìn)行抓包,才想起來安卓目前抓包非常困難,在電腦端用charles、fiddler這類代理是沒有用的;那就只能找安卓開發(fā)看這個(gè),我本來預(yù)期的是,在他那里,通過debug,要知道這個(gè)錯(cuò)誤到底是什么導(dǎo)致的,比如是https的哪個(gè)階段,是不是https證書的哪個(gè)字段有啥問題,結(jié)果,最終和我說的是,這個(gè)是底層okhttp的,沒法debug到那一層;我其實(shí)是對(duì)這塊持懷疑態(tài)度,肯定是有辦法的,但可能他不會(huì),從沒深入過https這層,所以就說他沒辦法繼續(xù)定位到更多信息了。
他么當(dāng)時(shí)火也大,但問題還是得解決(后面我看到貨拉拉那個(gè)文章里,其實(shí)是可以debug那部分代碼,不過確實(shí)是不在android.jar源碼里,在單獨(dú)的模塊中)。
安卓端沒法看,電腦端沒法用簡(jiǎn)單的方式抓包,我了解到的一些抓包的辦法都是很復(fù)雜,不搞安卓開發(fā)的話,光是搭環(huán)境都要搭半天那種;要么就是在手機(jī)上裝抓包軟件,但有些需要root,且能不能抓https這層檢查證書,我也持懷疑態(tài)度,我個(gè)人又是垃圾iphone,對(duì)安卓確實(shí)不熟悉。
唯一的辦法,就只有:wifi路由器上抓包,或者是找到目前負(fù)責(zé)https加解密的負(fù)載均衡設(shè)備的同事,來進(jìn)行抓包。
搜索引擎查找可能原因#
證書鎖定#
拿那個(gè)錯(cuò)誤,查了下原因,查到一篇貨拉拉的文章,感覺比較靠譜。
貨拉拉SSL證書踩坑之旅?貨拉拉SSL證書踩坑之旅
里面提到,app內(nèi)部可能內(nèi)置了服務(wù)端的證書,而app在訪問https后端建立https連接的過程中,服務(wù)端會(huì)把自己的證書(一般配置在nginx,我們這邊就是負(fù)載均衡設(shè)備,F5)返回給app,app檢查到返回的證書如果和本地內(nèi)置的不一致,就可能報(bào)那個(gè)錯(cuò);
java.security.cert.CertPathValidatorException: Trust anchor for certification path not found
這個(gè)專業(yè)術(shù)語叫做:證書鎖定. (證書鎖定SSL Pinning簡(jiǎn)介及用途 - 知乎)
這種就是可以防止中間人攻擊的,如fiddler、charles這類基于代理的,基本就屬于中間人攻擊,因?yàn)閏harles他們會(huì)把自己的證書給我們,我們內(nèi)置了證書的話,就會(huì)發(fā)現(xiàn)charles證書和內(nèi)置證書不一致,就可以主動(dòng)終止連接。
好些安卓的專業(yè)抓包方案,就是基于hook,把證書校驗(yàn)的那些代碼都給hook掉,這類方案對(duì)于非安卓開發(fā)人員還是困難了一點(diǎn),要一整套工具鏈,以后換個(gè)遙遙領(lǐng)先的話,可以好好折騰下。
另外,如果真用了證書鎖定,那么根據(jù)貨拉拉文章內(nèi)容,新證書可能少了某個(gè)字段,導(dǎo)致這個(gè)問題:
檢測(cè)網(wǎng)站#
亞數(shù)信息-SSL/TLS安全評(píng)估報(bào)告
可以輸入自己的網(wǎng)址,檢查下,不一定準(zhǔn),我們的問題當(dāng)時(shí)就沒查出來。
檢查安卓端配置#
可能有如下這個(gè)配置文件,看看里面的內(nèi)容,這里面也涉及一些trust-anchor的內(nèi)容:
負(fù)載均衡設(shè)備抓包#
排除后端嫌疑#
次日,我直接找了app端的leader,結(jié)果leader反饋說,app沒搞證書鎖定那些高級(jí)玩意,其他配置也檢查了,好像沒啥問題,所以無疾而終。
然后去找了負(fù)載均衡設(shè)備的同事,同事還是非常支持,所以,那天下午,我們就在一塊,在負(fù)載均衡設(shè)備上,抓了一下午的包。
他首先懷疑的是,后端服務(wù)返回的內(nèi)容是不是有問題,因?yàn)?#xff0c;用他手機(jī)嘗試時(shí),一會(huì)可以截屏,一會(huì)不可以,就是沒能穩(wěn)定復(fù)現(xiàn)。
于是就抓取負(fù)載設(shè)備和后端nginx之前的報(bào)文,這塊我們面臨一個(gè)問題,負(fù)載上流量很大,怎么區(qū)分出他手機(jī)的流量呢?尤其是現(xiàn)在好多手機(jī)都是優(yōu)先用Ipv6,而目前在百度這種查ip,基本只顯示了ipv4
那天我看同事用的ip138.com,我今天又搜了一個(gè):IP查詢(ipw.cn) | IPv6測(cè)試 | IPv6在線Ping測(cè)試 | IPv6網(wǎng)站檢測(cè) | IPv6網(wǎng)站測(cè)速 | IPv6地址查詢 | IP查詢(ipw.cn)
都還不錯(cuò)。
所以我們就抓負(fù)載和nginx之間的包,包里會(huì)有字段帶了我們的手機(jī)的出口ip:
就用這個(gè)字段篩選出我們的流量后,檢查發(fā)現(xiàn),后端返回的內(nèi)容沒啥問題。
后面和那個(gè)能穩(wěn)定復(fù)現(xiàn)的安卓設(shè)備比較,發(fā)現(xiàn)是同事手機(jī)的app版本低了,艸,升到最新版,就能穩(wěn)定復(fù)現(xiàn)了。
各種場(chǎng)景對(duì)比#
后面就開始對(duì)比,從公網(wǎng)過來,和從wifi過來的包;再就是,安卓設(shè)備端公網(wǎng)出口ip為ipv4和ipv6的,這么一組合,就有4種組合。
后面發(fā)現(xiàn),公網(wǎng)過來的,不管是ipv4還是ipv6,都沒問題;從wifi過來的,我們這邊測(cè)試,好像都是有問題的,但我們也抓包發(fā)現(xiàn)了其他人的請(qǐng)求,看著好像是從wifi來的,又沒問題的。
這期間其實(shí)探索了很多可能性,比如也檢查了waf設(shè)備(waf設(shè)備比負(fù)載均衡設(shè)備還要靠前,且waf工作在7層,也會(huì)涉及https的加解密,我是有懷疑過waf,但當(dāng)時(shí)看了waf的日志啥的,沒發(fā)現(xiàn)異常)
另外,這期間,我也在自己的云服務(wù)器上,嘗試了如下方式:
openssl s_client -debug -connect xxx.com.cn:443 tcpdump -i any host xxx.com.cn and tcp port 443 -w 443.pcap
和負(fù)載均衡端側(cè)的抓包進(jìn)行交叉對(duì)比。
對(duì)比的場(chǎng)景太多,都記不清了,但最終確定的是,wifi網(wǎng)絡(luò)下,出口ip是ipv4還是ipv6來著的時(shí)候,就有問題。
其實(shí)我一開始就是懷疑證書那塊可能有問題,但是,也不能在沒找到確切原因的時(shí)候,貿(mào)然對(duì)證書進(jìn)行操作,所以就和負(fù)載均衡設(shè)備的同事搞了一下午。
雖然當(dāng)時(shí)沒確定出根因,但收獲包括:
流量情況下,訪問xxx.com.cn:443是直接到xxx.com.cn:443的防火墻設(shè)備;
wifi下,訪問xxx.com.cn:443也是繞到了公司的互聯(lián)網(wǎng)出口,再去訪問xxx.com.cn:443的防火墻設(shè)備;
但是,可以肯定的是,這兩種情況下,xxx.com.cn:443的防火墻那邊,肯定是配置了不同的路由策略,兩者的網(wǎng)絡(luò)路徑應(yīng)該是不一樣的,這塊就還得找具體負(fù)責(zé)防火墻的同事來一起看。
本機(jī)模擬發(fā)現(xiàn)新端倪#
我們不是在負(fù)載均衡和nginx那層抓了包嗎,那層是明文的,我們就照著那個(gè)明文,錄入到本機(jī)的postman里,調(diào)用,發(fā)現(xiàn)是成功的。
后來,我想是不是postman沒校驗(yàn)證書,所以才成功的,然后找了找,發(fā)現(xiàn)確實(shí)有這么個(gè)選項(xiàng):
默認(rèn)是false,不校驗(yàn),我打卡后,再一請(qǐng)求,果然報(bào)錯(cuò)了,不過報(bào)的是服務(wù)端返回的證書缺少了中間證書。
所謂的中間證書,可以這么理解,目前世界上,有一批權(quán)威機(jī)構(gòu)(ROOT CA),他們負(fù)責(zé)給大家頒發(fā)https證書,頒發(fā)的證書會(huì)給到我們,然后我們就放到服務(wù)器上。
瀏覽器、手機(jī)等客戶端訪問我們時(shí),我們就把證書返回給瀏覽器等,此時(shí),他們?cè)趺粗牢覀兊淖C書是真的假的呢,就是靠證書里的頒發(fā)者字段,他們找到頒發(fā)者,再和自己瀏覽器內(nèi)置的或者操作系統(tǒng)中內(nèi)置的ROOT CA白名單做一個(gè)匹配,如果在本機(jī)內(nèi)置的ROOT CA白名單中,就可以認(rèn)為證書確實(shí)是這些權(quán)威機(jī)構(gòu)頒發(fā)的,值得信賴。(當(dāng)然,這只是其中的一個(gè)檢查項(xiàng),不是全部,比如還要檢查證書是否在有效期內(nèi),是否已經(jīng)被吊銷了)
但是哈,一般我們的證書,不會(huì)是這些ROOT CA直接頒發(fā)的,而是ROOT CA下屬的某個(gè)中間證書頒發(fā)的,以下面百度的為例:
此時(shí),百度服務(wù)端就必須返回baidu.com這個(gè)證書,但是它是由中間證書簽發(fā)的,而一般操作系統(tǒng)或者瀏覽器沒內(nèi)置中間證書那些機(jī)構(gòu),所以,服務(wù)端一般要把baidu.com以及中間證書機(jī)構(gòu)的證書,一并返回,這樣,才能一層層找到中間證書的簽發(fā)者,然后發(fā)現(xiàn)簽發(fā)者是root ca的話,就和本機(jī)的白名單做對(duì)比。
另外,我也在本機(jī)對(duì)了對(duì)照組,postman在兩種網(wǎng)絡(luò)下發(fā)請(qǐng)求:
- 本機(jī)pc在公司wifi下,此時(shí),走的是公司wifi
- 本機(jī)pc連接手機(jī)的熱點(diǎn),此時(shí),走的是流量網(wǎng)絡(luò)
對(duì)比了下,發(fā)現(xiàn)真的有問題:
在這兩種情況下,客戶端首先發(fā)請(qǐng)求(client hello)和服務(wù)端協(xié)商后續(xù)用哪個(gè)版本的tls協(xié)議??蛻舳税l(fā)出去的請(qǐng)求我對(duì)比了,除了隨機(jī)數(shù)部分,基本一致,但是,服務(wù)端最終協(xié)商出來的結(jié)果卻不一樣,一個(gè)是tls v1.2 ,一個(gè)是tls v.1.3
從這里也驗(yàn)證了,這個(gè)xxx.com.cn:443的接入這塊(一般接入那里應(yīng)該是路由器,但一般好像也具有防火墻的功能),會(huì)根據(jù)客戶端的網(wǎng)絡(luò)來源于wifi和流量,走了不同的路線。
這塊也得具體咨詢接入這塊的同事了。
補(bǔ)齊證書鏈解決問題#
結(jié)果我們后續(xù)還沒來得及去找接入的同事,負(fù)責(zé)負(fù)載均衡設(shè)備的同事跟我說,他把證書鏈補(bǔ)充完整了,讓我再試試。
所謂證書鏈補(bǔ)齊了的意思是,他之前就是負(fù)責(zé)將nginx層的證書挪到了負(fù)載均衡設(shè)備,在他完成這次變更后,https建立連接時(shí),每次服務(wù)端就只返回兩層證書了:
其實(shí)更好的辦法是用openssl工具,因?yàn)樯厦孢@個(gè)方法我發(fā)現(xiàn)也不一定準(zhǔn)確,我之前確實(shí)是發(fā)現(xiàn)有返回3層證書(含root ca)的時(shí)候,但我寫文章這會(huì),測(cè)試了下,發(fā)現(xiàn)又只有兩層了。
但是,用openssl進(jìn)行如下測(cè)試,都是能看到三層證書的:
openssl s_client -debug -connect xxx.com.cn:443 或 [root@VM-0-6-centos ~]# openssl s_client -showcerts -verify 5 -verify_return_error -connect xxx.com.cn:443 CONNECTED(00000003) depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA verify return:1 depth=1 C = US, O = DigiCert Inc, CN = DigiCert Secure Site CN CA G3 verify return:1 depth=0 C = CN, ST = xxxx, CN = *.xxx.com.cn verify return:1
對(duì)我對(duì)上述openssl命令,同時(shí)抓包的話,包內(nèi)顯示依然只有兩層證書,我之前看一本書里也說,一般也是不推薦返回ROOT CA的證書的,沒必要:
遺留問題#
因?yàn)閱栴}解決了,也就沒有再去找負(fù)責(zé)xxx.com.cn網(wǎng)絡(luò)接入的同事查問題了,大家事情也多,就這樣吧,事情搞定就行了。
但這也算隱形的坑,我猜測(cè)的話,可能是一個(gè)鏈路走了waf,一個(gè)鏈路沒走waf;所以最終一個(gè)協(xié)商出用tls v1.2,一個(gè)協(xié)商出用tls v1.3.
補(bǔ)充問題#
我翻到一個(gè)8月份的抓包文件:跟隨追蹤.pcap,里面的話,服務(wù)端確實(shí)是返回了3層證書的,包括了ROOT CA的,如下:
所以,我現(xiàn)在也有點(diǎn)疑問了,到底他么該返回幾層呢,只能說,如果大家遇到這類問題,可以往這個(gè)方面試一下,這個(gè)https水還是比較深的。
curl知識(shí)補(bǔ)充#
平時(shí)經(jīng)常用curl,但遇到https這種時(shí),一般會(huì)失敗;此時(shí),習(xí)慣性加個(gè)-k,跳過https證書校驗(yàn).
-k, --insecure (SSL) This option explicitly allows curl to perform "insecure" SSL connections and transfers. All SSL connections are attempted to be made secure by using the CA certificate bundle installed by default. This makes all connections considered "insecure" fail unless -k, --insecure is used. See this online resource for further details: http://curl.haxx.se/docs/sslcerts.html
[root@VM-0-6-centos ~]# curl https://www.baidu.com curl: (77) error setting certificate verify locations: CAfile: /etc/ssl/certs/ca-certificates.crt CApath: none [root@VM-0-6-centos ~]# curl https://www.baidu.com -k <!DOCTYPE html> ...
但是,這次是要解決https的問題,肯定不能跳過了,所以研究了下怎么把root ca裝到機(jī)器上,我是centos機(jī)器,我發(fā)現(xiàn)這樣就可以了:
root ca文件參考:https://curl.se/docs/caextract.html wget https://curl.se/ca/cacert.pem -k 下載到cacert.pem 然后指定下ca文件: [root@VM-0-6-centos ~]# curl --cacert cacert.pem https://www.baidu.com
參考文檔#
貨拉拉SSL證書踩坑之旅?貨拉拉SSL證書踩坑之旅
java.security.cert.CertPathValidatorException問題排查過程
如何在java類系統(tǒng)中導(dǎo)入自簽名證書以支持https的第三方系統(tǒng)的訪問
教大家在nginx中如何接入ldap認(rèn)證
技術(shù)分享 | MySQL : SSL 連接淺析-騰訊云開發(fā)者社區(qū)-騰訊云
https證書鏈校驗(yàn)失敗
那些年踩過HTTPS的坑(一)—— 證書鏈
App防抓包的 8 個(gè)實(shí)踐
網(wǎng)安工具 | 使用小黃鳥HttpCanary快速對(duì)安卓手機(jī)軟件流量抓包
Android13使用HttpCanary抓包
TLS原理與實(shí)踐(三)tls1.3
openssl: man openssl
openssl s_client : man s_client