寫在前面
近期某項目有一個業(yè)務拓展的需求斤程,需要將項目中單機房部署的模塊擴展成異地多機房部署粪糙。原先項目的模塊都部署在自建的機房A肠缔,有防火墻等相關安全策略的保護,相對比較安全宣吱,但現(xiàn)在網(wǎng)絡跨越了兩個公網(wǎng)通信的機房窃这,該如何保證傳輸安全和訪問控制呢?
HTTPS可以對服務器進行身份認證征候,同時也可以保證數(shù)據(jù)流量傳輸?shù)陌踩脊ィ苊庵虚g人攻擊,但這并不能滿足我們訪問權限控制的要求(我們的服務并不希望任何人都能訪問)疤坝。
解決以上問題有兩種思路兆解,一種是在應用層對模塊進行改造,使其支持訪問權限控制跑揉;另外一種則是雙向HTTPS锅睛,基于雙向HTTPS的特性來實現(xiàn)。出于是否能快速實現(xiàn)與成本考慮历谍,我們最終選擇了雙向HTTPS來實現(xiàn)這一需求现拒。
下面我們對如何實現(xiàn)這個需求與雙向HTTPS的原理做一個簡要介紹,希望給遇到類似問題的開發(fā)者提供一個思路供參考望侈。
HTTPS & 雙向HTTPS
HTTPS
HTTPS 全稱為 HyperText Transfer Protocol Secure印蔬,在HTTP的基礎上,集成了TLS/SSL傳輸層協(xié)議脱衙,以提供對網(wǎng)站服務器的身份認證扛点,保護交換數(shù)據(jù)的隱私與完整性。
雙向HTTPS
雙向HTTPS在單向HTTPS認證的基礎上岂丘,增加了服務端對客戶端身份認證的步驟,在進行通信前互相驗證對方身份眠饮,增加了網(wǎng)絡的安全性奥帘。
方案思考
網(wǎng)絡環(huán)境介紹
服務端與客戶端原先在同一機房內(nèi),只使用了HTTP協(xié)議來做數(shù)據(jù)傳輸仪召。
我們的目標方案則是保證跨域公網(wǎng)的兩個模塊能夠互相通信寨蹋,并在此基礎上確保輸過程的安全性與權限控制。
2.? 在1 的基礎上為客戶端增加Nginx做正向代理扔茅,將單向HTTPS升級為雙向HTTPS雙向HTTPS在單向HTTPS的基礎上已旧,多了服務端校驗客戶端證書的步驟。若服務端校驗客戶端證書失敗召娜,則在HTTPS握手階段服務端就將其拒絕运褪。這樣,就一定程度上實現(xiàn)了服務端的訪問權限控制。
涉及新增修改的內(nèi)容
1.? 新增代理層代理層包括客戶端和服務端的兩個代理服務器秸讹,此處選用Nginx
2.? 新增一個CA中心新增的CA中心主要用于為客戶端頒發(fā)證書( 服務端的HTTPS證書將選用商用CA證書)
配置雙向HTTPS的整體投入的整體投入和為模塊開發(fā)權限功能比起來檀咙,此方案的實現(xiàn)相對來說更簡單也更快捷。
環(huán)境搭建驗證
此處將使用openssl 與docker璃诀,在本地搭建一套方案中的模擬環(huán)境來驗證方案的可行性弧可。
證書生成
證書結構
證書結構方案所需的證書結構如下:
生成證書
生成私鑰
openssl genrsa -out ca.key 4096
# 生成自簽名根證書
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
生成服務端用證書
# 生成私鑰?
openssl genrsa -out server.key 4096
# 生成CSR證書請求
openssl req -new -key server.key -out server.csr
# 使用CA1根證書簽發(fā)證書
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650
生成客戶端證書的過程同生成服務端的過程相同,更換相應名稱即可
配置服務端Nginx
? 配置并啟動修改服務端 Nginx配置文件并啟動劣欢,具體關鍵配置如下:
服務端Nginx啟動
docker run \
? ? --name nginx_server \
? ? # 指定映射的端口
-p 30443:30443 \
? ? -d \
? ? #將相應的證書和配置文件掛載入容器
-v /Users/zhangchenyu/Documents/temp/nginx_test2/server/conf/nginx.conf:/etc/nginx/nginx.conf:ro \
? ? -v /Users/zhangchenyu/Documents/temp/nginx_test2/server/conf/index.html:/etc/nginx/html/index.html:ro \
? ? -v /Users/zhangchenyu/Documents/temp/nginx_test2/ca1:/etc/nginx/ca1:ro \
? ? -v /Users/zhangchenyu/Documents/temp/nginx_test2/ca2:/etc/nginx/ca2:ro \
? ? nginx
使用curl命令檢查Nginx是否已經(jīng)啟用雙向認證
證書注冊時使用了域名棕诵,將注冊時的相關域名添加到 /etc/hosts,否則使用本地ip無法訪問凿将。
直接訪問
curl \
? ? # 指定服務端證書的CA證書
--cacert ../ca1/ca.crt \
? ? https://test.server:30443
服務端返回的結果提示:要求的證書未發(fā)送校套。
指定客戶端發(fā)送證書
curl \
# 指定服務端證書的CA證書
--cacert ../ca1/ca.crt \
# 指定客戶端證書
--cert client.crt \
# 指定客戶端私鑰
--key ./client.key \
# 指定tls 協(xié)議版本
--tlsv1.2 \
? ? https://test.server:30443
調(diào)用結果:
當我們看到服務端返回了相應的頁面,說明服務端已經(jīng)開始使用雙向HTTPS丸相,對接收到的請求不再全部接受搔确,而是在HTTPS握手階段要求客戶端發(fā)送客戶端證書進行校驗,校驗通過的請求才進行處理灭忠。
配置客戶端Nginx的正向代理
配置并啟動
客戶端Nginx配置見下
注意:docker容器內(nèi)的Nginx 在配置轉發(fā)地址膳算,指定的IP端口需要為容器內(nèi)部IP端口。
客戶端Nginx 啟動
docker run \
? ? --name nginx_client\
? ? -d \
? ? # 指定端口映射
-p 9999:9999 \
? ? # 將相應的證書和配置文件掛載入容器
-v /Users/zhangchenyu/Documents/temp/nginx_temp2/client/conf/nginx.conf:/etc/nginx/nginx.conf:ro \
? ? -v /Users/zhangchenyu/Documents/temp/nginx_temp2/ca1:/etc/nginx/ca1:ro \
? ? -v /Users/zhangchenyu/Documents/temp/nginx_temp2/ca2:/etc/nginx/ca2:ro \
? ? nginx
使用 curl 命令檢查鏈路連通性
至此弛作,整套鏈路已經(jīng)搭建完成涕蜂,如果使用curl 命令能最后訪問到相應的服務端頁面,那說明我們目標達成
調(diào)用結果:
顯然映琳,客戶端使用HTTP就訪問到了終端服務机隙。通過以上步驟我們可以看到Nginx 的兩次代理,已經(jīng)完全實現(xiàn)了本次需求萨西。同時有鹿,我們發(fā)現(xiàn)通過Nginx配置的修改即可實現(xiàn)接口權限的控制,且保證網(wǎng)絡傳輸?shù)陌踩?/p>
抓包分析
最后谎脯,完成了配置也別忘了總結分析哦葱跋。我們對雙向HTTPS的握手和交互的過程進行抓包,抓包的同時簡要分析單雙向HTTPS的差異源梭,并對雙向HTTPS實現(xiàn)權限的控制過程予以了解娱俺。
單向HTTPS流量分析
隨意訪問一個HTTPS網(wǎng)站,并抓包废麻。具體內(nèi)容見下圖 :
1. 客戶端向服務端發(fā)送 Client Hello荠卷,其中包含一個隨機數(shù)A、支持的TLS版本烛愧、支持的加密套件等
2. 服務器響應給客戶端一個隨機數(shù)B油宜、選用的TLS協(xié)議版本掂碱、選用的加密套件、服務器證書验庙、DH公鑰等
此處Server Hello的包和證書顶吮、服務端DH公鑰等響應的數(shù)據(jù)包分成了兩個,而雙向HTTPS的數(shù)據(jù)包是單個的粪薛,這與單向雙向無關悴了,具體看服務器對HTTPS協(xié)議的實現(xiàn)方式。
3. 客戶端返回DH公鑰
4. 使用DH算法計算生成Pre-master secret违寿,并通過Master Secret生成器及隨機數(shù)A湃交、隨機數(shù)B、Pre-master Secret 生成最終加密通信所用的Master Secret
5. 客戶端和服務端互相告知對方自己狀態(tài)切換完成藤巢,并發(fā)送一條加密信息搞莺,以互相驗證雙方都擁有了正確的Master Secret
6. 握手完成,開始使用 Master Secret加密通信梳理下整體流程:
雙向HTTPS流量分析
此處抓包的內(nèi)容源自兩臺Nginx之間的流量
客戶端向服務端發(fā)送 Client Hello掂咒,其中包含隨機數(shù)A才沧、支持的TLS版本、支持的加密套件等
加密套件說明 例如:
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
ECDHE 秘鑰交換算法
RSA 身份驗證算法
AES128_GCM 批量加密算法
SHA256 消息認證碼算法
2.? 服務器響應給客戶端隨機數(shù)B绍刮、選用的TLS協(xié)議版本温圆、選用的加密套件、服務器證書孩革、DH算法公鑰岁歉、以及要求客戶端返回證書的請求等
–? ? 隨機數(shù)B、選用的TLS協(xié)議版本膝蜈、選用的加密套件
–? 服務端證書
–? 服務端DH公鑰
ServerKeyExchange是只有DH秘鑰交換算法才有的一步锅移,可以理解為這一步是為了計算得到Pre-master Secret;通過非對稱加密方式來握手獲取Pre-master Secret的加密套件不需要這一步饱搏。
–? 要求客戶端返回證書的請求
3.? 客戶端校驗證書的有效性
此處操作由Nginx 客戶端內(nèi)部完成非剃,未體現(xiàn)在抓包中。
4.? 客戶端返回客戶端自身證書, 以及對證書的簽名后, 通知服務端自己的加密策略已轉換推沸,以及第一條加密信息(用協(xié)商出的加密秘鑰加密)
–? 客戶端證書
此處區(qū)別于單向HTTPS努潘,客戶端發(fā)送的證書會被服務端Nginx配置的根證書進行校驗,只有驗證通過的客戶端才可進行下一步坤学。
– 客戶端DH算法公鑰
ClientKeyExchange,同ServerKeyExchange類似报慕,都是為了支援DH交換秘鑰
– 對證書的簽名
客戶端為了證明發(fā)出去的證書是自己的深浮,需要使用私鑰對證書進行簽名,以確認證書身份眠冈。
–? 通知服務端自己的加密策略已轉換(Client)
– Encrypted Handshake Message 客戶端第一條使用Master Secret加密的數(shù)據(jù)
Master Secret = MasterSecret生成器(隨機數(shù)A飞苇、 隨機數(shù)B菌瘫、 DH交換獲得的Pre-master Secret)
此消息發(fā)給服務端后,服務端會使用生成的Master Secret 進行解密布卡,確認客戶端已生成正確的Master Secret
5.? 服務端返回Session Ticket雨让、通知客戶端自己的加密策略已轉換以及第一條使用Master Secret加密的數(shù)據(jù)
– Session Ticket
服務端會緩存Master Secret 一段時間,只需要客戶端將Session Ticket 帶過來忿等,可以避免重復握手導致的資源開銷
– 通知服務端自己的加密策略已轉換(Server)
–? ? 服務端返回第一條使用Master Secret加密的數(shù)據(jù)栖忠,功能等同于服務端發(fā)送Encrypted Handshake Message,此項給客戶端是為了向客戶端證明自己也生成了正確的Master Secret贸街。
6. 開始使用Master Secret 加密數(shù)據(jù)并開始通信
梳理下整體流程:
流程對比
我們結合整體抓包和分析的流程可知庵寞,雙向HTTPS和單向HTTPS相比,多了對客戶端證書校驗薛匪、以及相應支援處理的步驟捐川。客戶端只有拿到了服務端CA頒發(fā)的證書逸尖,才能訪問到服務端古沥,這也是雙向HTTPS擁有一定權限控制功能的基礎。
總結
前文提及的改造需求娇跟,如果按照常規(guī)思路選擇改動業(yè)務代碼新增權限控制功能请垛,需要改動的整體流程較為復雜叨粘,開發(fā)成本也相對較重,為此我們借鑒雙向HTTPS的策略,通過修改配置方式快速地實現(xiàn)了該需求碘梢,避免了相關權限控制的重復勞動。
此外乳绕,HTTPS整體流程享潜,無論是單向還是雙向,都是互聯(lián)網(wǎng)技術領域的基礎保障基协,值得我們開發(fā)者繼續(xù)探究和學習其協(xié)議的相關細節(jié)歌亲。