做者簡介:
四拾一,個推高級開發工程師,精通Java、Go語言,全棧踐行者,對於數據安全、密碼學有較深刻的理解與實踐。html
近期某項目有一個業務拓展的需求,須要將項目中單機房部署的模塊擴展成異地多機房部署。原先項目的模塊都部署在自建的機房A,有防火牆等相關安全策略的保護,相對比較安全,但如今網絡跨越了兩個公網通訊的機房,該如何保證傳輸安全和訪問控制呢?nginx
HTTPS能夠對服務器進行身份認證,同時也能夠保證數據流量傳輸的安全,避免中間人攻擊,但這並不能知足咱們訪問權限控制的要求(咱們的服務並不但願任何人都能訪問)。算法
解決以上問題有兩種思路,一種是在應用層對模塊進行改造,使其支持訪問權限控制;另一種則是雙向HTTPS,基於雙向HTTPS的特性來實現。出因而否能快速實現與成本考慮,咱們最終選擇了雙向HTTPS來實現這一需求。docker
下面咱們對如何實現這個需求與雙向HTTPS的原理作一個簡要介紹,但願給遇到相似問題的開發者提供一個思路供參考。跨域
HTTPS 全稱爲 HyperText Transfer Protocol Secure,在HTTP的基礎上,集成了TLS/SSL傳輸層協議,以提供對網站服務器的身份認證,保護交換數據的隱私與完整性。緩存
雙向HTTPS在單向HTTPS認證的基礎上,增長了服務端對客戶端身份認證的步驟,在進行通訊前互相驗證對方身份,增長了網絡的安全性。安全
服務端與客戶端原先在同一機房內,只使用了HTTP協議來作數據傳輸。服務器
咱們的目標方案則是保證跨域公網的兩個模塊可以互相通訊,並在此基礎上確保輸過程的安全性與權限控制。網絡
此時客戶端訪問服務端的流量採用了HTTPS協議,保證了數據流量的安全,但暴露在公網上的服務端依然有被其餘人訪問的風險併發
雙向HTTPS在單向HTTPS的基礎上,多了服務端校驗客戶端證書的步驟。若服務端校驗客戶端證書失敗,則在HTTPS握手階段服務端就將其拒絕。這樣,就必定程度上實現了服務端的訪問權限控制。
代理層包括客戶端和服務端的兩個代理服務器,此處選用Nginx
新增的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
openssl req -new -key server.key -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650`
• 生成客戶端證書的過程同生成服務端的過程相同,更換相應名稱便可
• 配置並啓動
修改服務端 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配置見下
注意: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`
服務端返回的結果提示:要求的證書未發送。
• 指定客戶端發送證書
`
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網站,並抓包。具體內容見下圖 :
此處Server Hello的包和證書、服務端DH公鑰等響應的數據包分紅了兩個,而雙向HTTPS的數據包是單個的,這與單向雙向無關,具體看服務器對HTTPS協議的實現方式。
梳理下總體流程:
• 雙向HTTPS流量分析
此處抓包的內容源自兩臺Nginx之間的流量
客戶端向服務端發送 Client Hello,其中包含隨機數A、支持的TLS版本、支持的加密套件等
加密套件說明 例如: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
ECDHE 祕鑰交換算法
RSA 身份驗證算法
AES128_GCM 批量加密算法
SHA256 消息認證碼算法
– 隨機數B、選用的TLS協議版本、選用的加密套件
– 服務端證書
– 服務端DH公鑰
ServerKeyExchange是隻有DH祕鑰交換算法纔有的一步,能夠理解爲這一步是爲了計算獲得Pre-master Secret;經過非對稱加密方式來握手獲取Pre-master Secret的加密套件不須要這一步。
– 要求客戶端返回證書的請求
– 客戶端證書
此處區別於單向HTTPS,客戶端發送的證書會被服務端Nginx配置的根證書進行校驗,只有驗證經過的客戶端纔可進行下一步。
– 客戶端DH算法公鑰
ClientKeyExchange,同ServerKeyExchange相似,都是爲了支援DH交換祕鑰
– 對證書的簽名
客戶端爲了證實發出去的證書是本身的,須要使用私鑰對證書進行簽名,以確認證書身份。
– 通知服務端本身的加密策略已轉換(Client)
– Encrypted Handshake Message 客戶端第一條使用Master Secret加密的數據
Master Secret = MasterSecret生成器(隨機數A、 隨機數B、 DH交換得到的Pre-master Secret)
此消息發給服務端後,服務端會使用生成的Master Secret 進行解密,確認客戶端已生成正確的Master Secret
– Session Ticket
服務端會緩存Master Secret 一段時間,只須要客戶端將Session Ticket 帶過來,能夠避免重複握手致使的資源開銷
– 通知服務端本身的加密策略已轉換(Server)
– 服務端返回第一條使用Master Secret加密的數據,功能等同於服務端發送Encrypted Handshake Message,此項給客戶端是爲了向客戶端證實本身也生成了正確的Master Secret。
梳理下總體流程:
• 流程對比
咱們結合總體抓包和分析的流程可知,雙向HTTPS和單向HTTPS相比,多了對客戶端證書校驗、以及相應支援處理的步驟。客戶端只有拿到了服務端CA頒發的證書,才能訪問到服務端,這也是雙向HTTPS擁有必定權限控制功能的基礎。
前文說起的改造需求,若是按照常規思路選擇改動業務代碼新增權限控制功能,須要改動的總體流程較爲複雜,開發成本也相對較重,爲此咱們借鑑雙向HTTPS的策略,經過修改配置方式快速地實現了該需求,避免了相關權限控制的重複勞動。
此外,HTTPS總體流程,不管是單向仍是雙向,都是互聯網技術領域的基礎保障,值得咱們開發者繼續探究和學習其協議的相關細節。