如何用雙向HTTPS進行「偷懶」

image

做者簡介:
四拾一,個推高級開發工程師,精通Java、Go語言,全棧踐行者,對於數據安全、密碼學有較深刻的理解與實踐。html

01

寫在前面

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

HTTPS能夠對服務器進行身份認證,同時也能夠保證數據流量傳輸的安全,避免中間人攻擊,但這並不能知足咱們訪問權限控制的要求(咱們的服務並不但願任何人都能訪問)。算法

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

下面咱們對如何實現這個需求與雙向HTTPS的原理作一個簡要介紹,但願給遇到相似問題的開發者提供一個思路供參考。跨域

02

HTTPS & 雙向HTTPS

HTTPS

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

雙向HTTPS

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

image

03

方案思考

網絡環境介紹

服務端與客戶端原先在同一機房內,只使用了HTTP協議來作數據傳輸。服務器

image

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

image

解決思路
  1. 爲服務端增長Nginx作反向HTTPS代理

此時客戶端訪問服務端的流量採用了HTTPS協議,保證了數據流量的安全,但暴露在公網上的服務端依然有被其餘人訪問的風險併發

image

  1. 在1 的基礎上爲客戶端增長Nginx作正向代理,將單向HTTPS升級爲雙向HTTPS

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

涉及新增修改的內容
  1. 新增代理層

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

  1. 新增一個CA中心

新增的CA中心主要用於爲客戶端頒發證書( 服務端的HTTPS證書將選用商用CA證書)
配置雙向HTTPS的總體投入的總體投入和爲模塊開發權限功能比起來,此方案的實現相對來講更簡單也更快捷。

環境搭建驗證

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

證書生成

證書結構
方案所需的證書結構以下:
image

• 生成證書

`# 生成私鑰
openssl genrsa -out ca.key 4096

生成自簽名根證書

openssl req -new -x509 -days 3650 -key ca.key -out ca.crt`

image

• 生成服務端用證書
`

生成私鑰

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`

image
• 生成客戶端證書的過程同生成服務端的過程相同,更換相應名稱便可

配置服務端Nginx

• 配置並啓動
修改服務端 Nginx配置文件並啓動,具體關鍵配置以下:
image

•服務端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沒法訪問。
image

• 直接訪問
`
curl \

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

image

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

`
curl \

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

• 調用結果:
image

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

配置客戶端Nginx的正向代理

配置並啓動
•客戶端Nginx配置見下
image

注意: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沒法訪問。
image

• 直接訪問

`
curl \

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

image

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

`
curl \

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

• 調用結果:
image

顯然,客戶端使用HTTP就訪問到了終端服務。經過以上步驟咱們能夠看到Nginx 的兩次代理,已經徹底實現了本次需求。同時,咱們發現經過Nginx配置的修改便可實現接口權限的控制,且保證網絡傳輸的安全。

抓包分析

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

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

image

  1. 客戶端向服務端發送 Client Hello,其中包含一個隨機數A、支持的TLS版本、支持的加密套件等
  2. 服務器響應給客戶端一個隨機數B、選用的TLS協議版本、選用的加密套件、服務器證書、DH公鑰等

image

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

image

  1. 客戶端返回DH公鑰

image

  1. 使用DH算法計算生成Pre-master secret,並經過Master Secret生成器及隨機數A、隨機數B、Pre-master Secret 生成最終加密通訊所用的Master Secret
  2. 客戶端和服務端互相告知對方本身狀態切換完成,併發送一條加密信息,以互相驗證雙方都擁有了正確的Master Secret

image

  1. 握手完成,開始使用 Master Secret加密通訊

梳理下總體流程:

image

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

image

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

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

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

– 隨機數B、選用的TLS協議版本、選用的加密套件

image
– 服務端證書

– 服務端DH公鑰

image

ServerKeyExchange是隻有DH祕鑰交換算法纔有的一步,能夠理解爲這一步是爲了計算獲得Pre-master Secret;經過非對稱加密方式來握手獲取Pre-master Secret的加密套件不須要這一步。

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

  1. 客戶端校驗證書的有效性
    此處操做由Nginx 客戶端內部完成,未體如今抓包中。
  2. 客戶端返回客戶端自身證書, 以及對證書的簽名後, 通知服務端本身的加密策略已轉換,以及第一條加密信息(用協商出的加密祕鑰加密)

– 客戶端證書

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

– 客戶端DH算法公鑰

image

ClientKeyExchange,同ServerKeyExchange相似,都是爲了支援DH交換祕鑰

– 對證書的簽名

image

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

image

– Encrypted Handshake Message 客戶端第一條使用Master Secret加密的數據
image

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

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

  1. 服務端返回Session Ticket、通知客戶端本身的加密策略已轉換以及第一條使用Master Secret加密的數據

– Session Ticket
image
服務端會緩存Master Secret 一段時間,只須要客戶端將Session Ticket 帶過來,能夠避免重複握手致使的資源開銷

– 通知服務端本身的加密策略已轉換(Server)
image
– 服務端返回第一條使用Master Secret加密的數據,功能等同於服務端發送Encrypted Handshake Message,此項給客戶端是爲了向客戶端證實本身也生成了正確的Master Secret。

  1. 開始使用Master Secret 加密數據並開始通訊

image

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

04

總結

前文說起的改造需求,若是按照常規思路選擇改動業務代碼新增權限控制功能,須要改動的總體流程較爲複雜,開發成本也相對較重,爲此咱們借鑑雙向HTTPS的策略,經過修改配置方式快速地實現了該需求,避免了相關權限控制的重複勞動。

此外,HTTPS總體流程,不管是單向仍是雙向,都是互聯網技術領域的基礎保障,值得咱們開發者繼續探究和學習其協議的相關細節。

image

相關文章
相關標籤/搜索