一 、HTTPS連接過(guò)程及中間人攻擊原理
https協(xié)議就是http+ssl協(xié)議,如下圖所示為其連接過(guò)程:

1.https請(qǐng)求
客戶端向服務(wù)端發(fā)送https請(qǐng)求;
2.生成公鑰和私鑰
服務(wù)端收到請(qǐng)求之后,生成公鑰和私鑰。公鑰相當(dāng)于是鎖,私鑰相當(dāng)于是鑰匙,只有私鑰才能夠打開公鑰鎖住的內(nèi)容;
3.返回公鑰
服務(wù)端將公鑰(證書)返回給客戶端,公鑰里面包含有很多信息,比如證書的頒發(fā)機(jī)構(gòu)、過(guò)期時(shí)間等等;
4.客戶端驗(yàn)證公鑰
客戶端收到公鑰之后,首先會(huì)驗(yàn)證其是否有效,如頒發(fā)機(jī)構(gòu)或者過(guò)期時(shí)間等,如果發(fā)現(xiàn)有問(wèn)題就會(huì)拋出異常,提示證書存在問(wèn)題。如果沒(méi)有問(wèn)題,那么就生成一個(gè)隨機(jī)值,作為客戶端的密鑰,然后用服務(wù)端的公鑰加密;
5.發(fā)送客戶端密鑰
客戶端用服務(wù)端的公鑰加密密鑰,然后發(fā)送給服務(wù)端。
6.服務(wù)端收取密鑰,對(duì)稱加密內(nèi)容
服務(wù)端收到經(jīng)過(guò)加密的密鑰,然后用私鑰將其解密,得到客戶端的密鑰,然后服務(wù)端把要傳輸?shù)膬?nèi)容和客戶端的密鑰進(jìn)行對(duì)稱加密,這樣除非知道密鑰,否則無(wú)法知道傳輸?shù)膬?nèi)容。
7.加密傳輸
服務(wù)端將經(jīng)過(guò)加密的內(nèi)容傳輸給客戶端。
8.獲取加密內(nèi)容,解密
客戶端獲取加密內(nèi)容后,用之前生成的密鑰對(duì)其進(jìn)行解密,獲取到內(nèi)容。
中間人劫持攻擊
https也不是絕對(duì)安全的,如下圖所示為中間人劫持攻擊,中間人可以獲取到客戶端與服務(wù)器之間所有的通信內(nèi)容。
HTTPS連接過(guò)程以及中間人攻擊劫持
中間人截取客戶端發(fā)送給服務(wù)器的請(qǐng)求,然后偽裝成客戶端與服務(wù)器進(jìn)行通信;將服務(wù)器返回給客戶端的內(nèi)容發(fā)送給客戶端,偽裝成服務(wù)器與客戶端進(jìn)行通信。
通過(guò)這樣的手段,便可以獲取客戶端和服務(wù)器之間通信的所有內(nèi)容。
使用中間人攻擊手段,必須要讓客戶端信任中間人的證書,如果客戶端不信任,則這種攻擊手段也無(wú)法發(fā)揮作用。
二、中間人攻擊的預(yù)防
造成中間人劫持的原因是 沒(méi)有對(duì)服務(wù)端證書及域名做校驗(yàn)或者校驗(yàn)不完整,為了方便,直接采用開源框架默認(rèn)的校驗(yàn)方式進(jìn)行https請(qǐng)求
如volley
HTTPS連接過(guò)程以及中間人攻擊劫持
HTTPS連接過(guò)程以及中間人攻擊劫持
OKhttp3.0
HTTPS連接過(guò)程以及中間人攻擊劫持
預(yù)防方法:
預(yù)防方式有兩種
1 、針對(duì)安全性要求比較高的 app,可采取客戶端預(yù)埋證書的方式鎖死證書,只有當(dāng)客戶端證書和服務(wù)端的證書完全一致的情況下才允許通信,如一些銀行類的app,但這種方式面臨一個(gè)問(wèn)題,證書過(guò)期的問(wèn)題,因證書有一定的有效期,當(dāng)預(yù)埋證書過(guò)期了,只有通過(guò)強(qiáng)制更新或者要求用戶下載證書來(lái)解決。
以volley為例:校驗(yàn)的實(shí)現(xiàn)方式如下
通過(guò)預(yù)埋證書創(chuàng)建 SSLSocketFactory;
private static SSLSocketFactory buildSSLSocketFactory(Context context,
int certRawResId) {
KeyStore keyStore = null;
try {
keyStore = buildKeyStore(context, certRawResId);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = null;
try {
tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLS");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
try {
sslContext.init(null, tmf.getTrustManagers(), null);
} catch (KeyManagementException e) {
e.printStackTrace();
}
return sslContext.getSocketFactory();
生成 經(jīng)過(guò)ssl校驗(yàn)及 域名校驗(yàn)的connection
HTTPS連接過(guò)程以及中間人攻擊劫持
HTTPS連接過(guò)程以及中間人攻擊劫持
2 針對(duì)安全性要求一般的app,可采用通過(guò)校驗(yàn)域名,證書有效性、證書關(guān)鍵信息及證書鏈的方式
以volley為例,重寫HTTPSTrustManager 中的checkServerTrusted 方法,同時(shí)開啟域名強(qiáng)校驗(yàn)
三、Webview的HTTPS安全
目前很多應(yīng)用都用webview加載H5頁(yè)面,如果服務(wù)端采用的是可信CA頒發(fā)的證書,在 webView.setWebViewClient(webviewClient) 時(shí)重載 WebViewClient的onReceivedSslError() ,如果出現(xiàn)證書錯(cuò)誤,直接調(diào)用handler.proceed()會(huì)忽略錯(cuò)誤繼續(xù)加載證書有問(wèn)題的頁(yè)面,如果調(diào)用handler.cancel()可以終止加載證書有問(wèn)題的頁(yè)面,證書出現(xiàn)問(wèn)題了,可以提示用戶風(fēng)險(xiǎn),讓用戶選擇加載與否,如果是需要安全級(jí)別比較高,可以直接終止頁(yè)面加載,提示用戶網(wǎng)絡(luò)環(huán)境有風(fēng)險(xiǎn):
HTTPS連接過(guò)程以及中間人攻擊劫持
不建議直接用handler.proceed()。如果webview加載https需要強(qiáng)校驗(yàn)服務(wù)端證書,可以在 onPageStarted() 中用 HttpsURLConnection 強(qiáng)校驗(yàn)證書的方式來(lái)校驗(yàn)服務(wù)端證書,如果校驗(yàn)不通過(guò)停止加載網(wǎng)頁(yè)。當(dāng)然這樣會(huì)拖慢網(wǎng)頁(yè)的加載速度,需要進(jìn)一步優(yōu)化,具體優(yōu)化的辦法不在本次討論范圍,這里也不詳細(xì)講解了。