鏈接:/question/52320264/answer/130042542
來源:知乎。
版權歸作者所有。商業轉載請聯系作者授權,非商業轉載請註明出處。
Ssl會話多路復用
首先,使用https進行通信時,消耗和延遲增加的主要是在SSL握手階段。在這個階段,客戶端和服務器端需要協商壹些參數,比如雙方支持的加解密套件,最後生成壹個會話密鑰。
這個過程是壹個非對稱加密過程,主要是為了保護會話密鑰的生成。
由於非對稱加密的過程非常耗時,計算量也很大,所以後續的所有通信都是對稱加密,比如使用AES-128和會話密鑰來加密通信。
這個協商會話密鑰的過程是主要的開銷。為了避免每次會話中的這種開銷,會話重用由此而來。
在這個階段,支持兩種會話重用機制:會話id和會話票證。會話id機制由服務器存儲主要信息,會話票證由客戶端存儲主要信息。
會話id機制:如果客戶端想要重用會話,它會在客戶端hello消息中發送壹個會話id號,該id號對應於之前的會話信息,如pre-master secret、master secret和session key,這些信息需要通過“昂貴的”握手階段重新協商。如果服務器支持客戶端的想法,就會根據這個會話id號在本地查詢id號對應的當前會話信息。如果找到了這些信息,比如會話密鑰,那麽在serverhello中發送回客戶端的消息中將會包含通用會話id字段,表明我支持您的id重用。然後他們繞過了重新協商會話密鑰的過程,直接重用當前的會話密鑰,從而達到了會話重用的目的。
我們可以看到這種機制的特點:服務器需要保存壹個id和會話狀態的字典,但是當客戶端比較多的時候,這種情況大大增加了服務器的壓力,並且不具有可擴展性。而且如果是服務器集群,會話id復用的成功率也會降低,因為客戶端第二次通信時服務器可能不會存儲這個id號,導致復用失敗,還是要重新握手。
在這種情況下,可以說是將id和會話狀態存儲在壹個公共的存儲介質中(比如redis/memcached),這樣所有的服務器節點都可以查詢這些id對應的狀態,提高了復用率。目前,我們公司使用redis來存儲它們。
會話票機制:由於會話id不可擴展的缺點,後來出現了票的重用機制,給客戶端帶來了存儲壓力。也就是說,在壹個新的會話之後,服務器使用壹個自由已知的密鑰ticket key對會話狀態進行加密,然後將其發送給客戶端,客戶端保存該票證。下次客戶端與服務器建立ssl連接時,它會將此票證發送給服務器。因為只有服務器才能解鎖這張票,而且票密鑰不泄露,安全性有壹定保證。服務器解鎖票證後,取出會話狀態(包括會話密鑰等。)中,然後開始使用這個會話密鑰進行加密通信(客戶端當然知道當前的會話密鑰)。
這種方法也達到了會話復用的目的,將所有的存儲壓力轉移到客戶端,具有可擴展性。現階段,所有主流瀏覽器都首先支持票的復用。
但是這種方法還是有壹定的缺陷:服務器集群中的所有節點都必須使用同壹個票密鑰,時間長了會不安全。所以這個票密鑰是需要輪換的,但重點是需要兼容舊票密鑰,用新票密鑰加密解密,只用舊票密鑰解密,最後逐步淘汰舊票密鑰。從而實現安全的復用效果。
目前這種票密鑰的輪換方式有很多種。我公司目前采用的是cloudflare的開源輪換方式,每小時輪換壹次。具體代碼可以在github上找到。