1. 程式人生 > 實用技巧 >如何用雙向HTTPS進行“偷懶”

如何用雙向HTTPS進行“偷懶”

作者簡介:
四拾一,個推高階開發工程師,精通Java、Go語言,全棧踐行者,對於資料安全、密碼學有較深入的理解與實踐。

01

寫在前面

近期某專案有一個業務拓展的需求,需要將專案中單機房部署的模組擴充套件成異地多機房部署。原先專案的模組都部署在自建的機房A,有防火牆等相關安全策略的保護,相對比較安全,但現在網路跨越了兩個公網通訊的機房,該如何保證傳輸安全和訪問控制呢?

HTTPS可以對伺服器進行身份認證,同時也可以保證資料流量傳輸的安全,避免中間人攻擊,但這並不能滿足我們訪問許可權控制的要求(我們的服務並不希望任何人都能訪問)。

解決以上問題有兩種思路,一種是在應用層對模組進行改造,使其支援訪問許可權控制;另外一種則是雙向HTTPS,基於雙向HTTPS的特性來實現。出於是否能快速實現與成本考慮,我們最終選擇了雙向HTTPS來實現這一需求。

下面我們對如何實現這個需求與雙向HTTPS的原理做一個簡要介紹,希望給遇到類似問題的開發者提供一個思路供參考。

02

HTTPS & 雙向HTTPS

HTTPS

HTTPS 全稱為 HyperText Transfer Protocol Secure,在HTTP的基礎上,集成了TLS/SSL傳輸層協議,以提供對網站伺服器的身份認證,保護交換資料的隱私與完整性。

雙向HTTPS

雙向HTTPS在單向HTTPS認證的基礎上,增加了服務端對客戶端身份認證的步驟,在進行通訊前互相驗證對方身份,增加了網路的安全性。

03

方案思考

網路環境介紹

服務端與客戶端原先在同一機房內,只使用了HTTP協議來做資料傳輸。

我們的目標方案則是保證跨域公網的兩個模組能夠互相通訊,並在此基礎上確保輸過程的安全性與許可權控制。

解決思路

  1. 為服務端增加Nginx做反向HTTPS代理
    此時客戶端訪問服務端的流量採用了HTTPS協議,保證了資料流量的安全,但暴露在公網上的服務端依然有被其他人訪問的風險

  1. 在1 的基礎上為客戶端增加Nginx做正向代理,將單向HTTPS升級為雙向HTTPS

雙向HTTPS在單向HTTPS的基礎上,多了服務端校驗客戶端證書的步驟。若服務端校驗客戶端證書失敗,則在HTTPS握手階段服務端就將其拒絕。這樣,就一定程度上實現了服務端的訪問許可權控制。

涉及新增修改的內容

  1. 新增代理層
    代理層包括客戶端和服務端的兩個代理伺服器,此處選用Nginx

  2. 新增一個CA中心
    新增的CA中心主要用於為客戶端頒發證書( 服務端的HTTPS證書將選用商用CA證書)
    配置雙向HTTPS的整體投入的整體投入和為模組開發許可權功能比起來,此方案的實現相對來說更簡單也更快捷。

環境搭建驗證

此處將使用openssl 與docker,在本地搭建一套方案中的模擬環境來驗證方案的可行性。

證書生成

證書結構
方案所需的證書結構如下:

• 生成證書

`# 生成私鑰
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根證書籤發證書

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是否已經啟用雙向認證
證書註冊時使用了域名,將註冊時的相關域名新增到 /etc/hosts,否則使用本地ip無法訪問。

• 直接訪問
curl \ # 指定服務端證書的CA證書 --cacert ../ca1/ca.crt \ https://test.server:30443

服務端返回的結果提示:要求的證書未傳送。
• 指定客戶端傳送證書

curl \ # 指定服務端證書的CA證書 --cacert ../ca1/ca.crt \ # 指定客戶端證書 --cert client.crt \ # 指定客戶端私鑰 --key ./client.key \ # 指定tls 協議版本 --tlsv1.2 \ https://test.server:30443

• 呼叫結果:

當我們看到服務端返回了相應的頁面,說明服務端已經開始使用雙向HTTPS,對接收到的請求不再全部接受,而是在HTTPS握手階段要求客戶端傳送客戶端證書進行校驗,校驗通過的請求才進行處理。

配置客戶端Nginx的正向代理

配置並啟動
•客戶端Nginx配置見下

注意:docker容器內的Nginx 在配置轉發地址,指定的IP埠需要為容器內部IP埠。

• 客戶端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是否已經啟用雙向認證
證書註冊時使用了域名,將註冊時的相關域名新增到 /etc/hosts,否則使用本地ip無法訪問。

• 直接訪問

curl \ # 指定服務端證書的CA證書 --cacert ../ca1/ca.crt \ https://test.server:30443

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-bMdTUHyK-1602177408571)(/img/bVceVRD)] ](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/81b17287c3904c7b8501403bff5033f5~tplv-k3u1fbpfcp-zoom-1.image)

服務端返回的結果提示:要求的證書未傳送。
• 指定客戶端傳送證書

curl \ # 指定服務端證書的CA證書 --cacert ../ca1/ca.crt \ # 指定客戶端證書 --cert client.crt \ # 指定客戶端私鑰 --key ./client.key \ # 指定tls 協議版本 --tlsv1.2 \ https://test.server:30443

• 呼叫結果:

顯然,客戶端使用HTTP就訪問到了終端服務。通過以上步驟我們可以看到Nginx 的兩次代理,已經完全實現了本次需求。同時,我們發現通過Nginx配置的修改即可實現介面許可權的控制,且保證網路傳輸的安全。

抓包分析

最後,完成了配置也別忘了總結分析哦。我們對雙向HTTPS的握手和互動的過程進行抓包,抓包的同時簡要分析單雙向HTTPS的差異,並對雙向HTTPS實現許可權的控制過程予以瞭解。

• 單向HTTPS流量分析
隨意訪問一個HTTPS網站,並抓包。具體內容見下圖 :

  1. 客戶端向服務端傳送 Client Hello,其中包含一個隨機數A、支援的TLS版本、支援的加密套件等

  2. 伺服器響應給客戶端一個隨機數B、選用的TLS協議版本、選用的加密套件、伺服器證書、DH公鑰等

此處Server Hello的包和證書、服務端DH公鑰等響應的資料包分成了兩個,而雙向HTTPS的資料包是單個的,這與單向雙向無關,具體看伺服器對HTTPS協議的實現方式。

  1. 客戶端返回DH公鑰

  1. 使用DH演算法計算生成Pre-master secret,並通過Master Secret生成器及隨機數A、隨機數B、Pre-master Secret 生成最終加密通訊所用的Master Secret

  2. 客戶端和服務端互相告知對方自己狀態切換完成,併發送一條加密資訊,以互相驗證雙方都擁有了正確的Master Secret

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-kmfjGGkB-1602177408581)(/img/bVceVTZ)]](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9dbb16403e284efe846b8403c8a5fc5c~tplv-k3u1fbpfcp-zoom-1.image)

  1. 握手完成,開始使用 Master Secret加密通訊
    梳理下整體流程:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-XLTZN0JQ-1602177408584)(/img/bVceVT2)]](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7cc2797f57a24017b1ea9f1456c1b87b~tplv-k3u1fbpfcp-zoom-1.image)

• 雙向HTTPS流量分析
此處抓包的內容源自兩臺Nginx之間的流量

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-wk6KMA51-1602177408586)(/img/bVceVT4)]](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/91b8a4c75bfb416980c30802e96f60a7~tplv-k3u1fbpfcp-zoom-1.image)

客戶端向服務端傳送 Client Hello,其中包含隨機數A、支援的TLS版本、支援的加密套件等

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-JlfnJMlB-1602177408592)(/img/bVceVT8)]](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ed4f67c983e14fe8ae1f8722567b5aec~tplv-k3u1fbpfcp-zoom-1.image)

加密套件說明 例如: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
ECDHE 祕鑰交換演算法
RSA 身份驗證演算法
AES128_GCM 批量加密演算法
SHA256 訊息認證碼演算法

  1. 伺服器響應給客戶端隨機數B、選用的TLS協議版本、選用的加密套件、伺服器證書、DH演算法公鑰、以及要求客戶端返回證書的請求等
    – 隨機數B、選用的TLS協議版本、選用的加密套件

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-zGyzeK6u-1602177408594)(/img/bVceVT9)]](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2780ecb32f444d4ab74794fe707f0625~tplv-k3u1fbpfcp-zoom-1.image)

– 服務端證書
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-f54AhHh9-1602177408595)(/img/bVceVUc)]
](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5cc40903089d417eaa9d95aa58be2196~tplv-k3u1fbpfcp-zoom-1.image)

– 服務端DH公鑰

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-RfOQjTYh-1602177408597)(/img/bVceVUa)]](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/940cb04a5be242bfa6ee99c59d28b97b~tplv-k3u1fbpfcp-zoom-1.image)

ServerKeyExchange是隻有DH祕鑰交換演算法才有的一步,可以理解為這一步是為了計算得到Pre-master Secret;通過非對稱加密方式來握手獲取Pre-master Secret的加密套件不需要這一步。

– 要求客戶端返回證書的請求

  1. 客戶端校驗證書的有效性
    此處操作由Nginx 客戶端內部完成,未體現在抓包中。

  2. 客戶端返回客戶端自身證書, 以及對證書的簽名後, 通知服務端自己的加密策略已轉換,以及第一條加密資訊(用協商出的加密祕鑰加密)

– 客戶端證書

此處區別於單向HTTPS,客戶端傳送的證書會被服務端Nginx配置的根證書進行校驗,只有驗證通過的客戶端才可進行下一步。

– 客戶端DH演算法公鑰

ClientKeyExchange,同ServerKeyExchange類似,都是為了支援DH交換祕鑰

– 對證書的簽名

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-7opxHbuw-1602177408609)(/img/bVceVUJ)]](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1c2dbe864248448782ca774a106c3b08~tplv-k3u1fbpfcp-zoom-1.image)

客戶端為了證明發出去的證書是自己的,需要使用私鑰對證書進行簽名,以確認證書身份。
– 通知服務端自己的加密策略已轉換(Client)

– Encrypted Handshake Message 客戶端第一條使用Master Secret加密的資料

Master Secret = MasterSecret生成器(隨機數A、 隨機數B、 DH交換獲得的Pre-master Secret)

此訊息發給服務端後,服務端會使用生成的Master Secret 進行解密,確認客戶端已生成正確的Master Secret

  1. 服務端返回Session Ticket、通知客戶端自己的加密策略已轉換以及第一條使用Master Secret加密的資料
    – Session Ticket

服務端會快取Master Secret 一段時間,只需要客戶端將Session Ticket 帶過來,可以避免重複握手導致的資源開銷

– 通知服務端自己的加密策略已轉換(Server)

– 服務端返回第一條使用Master Secret加密的資料,功能等同於服務端傳送Encrypted Handshake Message,此項給客戶端是為了向客戶端證明自己也生成了正確的Master Secret。

  1. 開始使用Master Secret 加密資料並開始通訊

梳理下整體流程:

• 流程對比
我們結合整體抓包和分析的流程可知,雙向HTTPS和單向HTTPS相比,多了對客戶端證書校驗、以及相應支援處理的步驟。客戶端只有拿到了服務端CA頒發的證書,才能訪問到服務端,這也是雙向HTTPS擁有一定許可權控制功能的基礎。

04

總結

前文提及的改造需求,如果按照常規思路選擇改動業務程式碼新增許可權控制功能,需要改動的整體流程較為複雜,開發成本也相對較重,為此我們借鑑雙向HTTPS的策略,通過修改配置方式快速地實現了該需求,避免了相關許可權控制的重複勞動。

此外,HTTPS整體流程,無論是單向還是雙向,都是網際網路技術領域的基礎保障,值得我們開發者繼續探究和學習其協議的相關細節。