這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
1 前言
網上介紹https的文章並不多,更鮮有分享在大型互連網網站部署https的實踐經驗,我們在考慮部署https時也有重重的疑惑。
本文為大家介紹百度HTTPS的實踐和一些權衡, 希望以此拋磚引玉。
本文最早發表於百度營運部官方部落格
2 協議層以外的實踐工作
2.1 全站覆蓋https的理由
很多剛接觸https的會思考,我是不是只要網站的主網域名稱換了https就可以?答案是不行。
https的目的就是保證傳輸過程的安全,如果只有主網域名稱上了https,但是主網域名稱載入的資源,比如js,css,圖片沒有上https,會怎麼樣?
從效果上來說,沒有達到保證網站傳輸過程安全的目的,因為你的js,css,圖片仍然有被劫持的可能性,如果這些內容被篡改/嗅探了,那麼https的意義就失去了。
瀏覽器在設計上早就考慮的這樣的情況,會有相應的提示。具體的實現依賴瀏覽器,例如地址欄鎖形標記從綠色變為黃色, 阻止這次請求,或者直接彈出非常影響使用者體驗的提示(主要是IE),使用者會感覺厭煩,疑惑和擔憂安全性。
很多使用者看見這個連結會習慣性的點”是”,這樣非https的資源就被禁止載入了。非ie的瀏覽器很多也會阻止載入一些危害程度較高的非https資源(例如js)。我們發現移動端瀏覽器的限制目前會略松一些。
所以這裡要是沒做好,很多情況連網站的準系統都沒法正常使用。
2.2 網站的區別
很多人剛接觸https的時候,覺得不就是部署認證,讓webserver支援https就行了嗎。
實際上對於不同的網站來說,https的部署方式和難度有很大的區別。對於一個大型網站來說,讓webserver支援https,以及對webserver在https協議特性上做一些最佳化,在遷移的工作比重上,可能只佔到20%-40%。
我們考慮下以下幾種情況下,部署https的方案。
2.2.1 簡單的個人網站
簡單的定義:資源只從本站的主域或者主域的子網域名稱載入。
比如axyz的個人blog,網域名稱是axyzblog.com。載入主網域名稱下的js和圖片。
這樣的站部署https,在已有認證且webserver支援的情況下,只需要把主網域名稱替換為https接入,然後把資源串連修改為https://或者 //。
2.2.2 複雜的個人網站
複雜的定義:資源需要從外部網域名稱載入。
這樣就比較麻煩了,主域資源容易適配https,在cdn上載入的資源還需要cdn服務商支援https。目前各大cdn的服務商正在逐漸提供https的支援,需要遷移的朋友可以看看自己使用的cdn是否提供了這項能力。一些cdn會對https流量額外收費。
Cdn使用https常見的方案有:
1. 網站主提供私密金鑰給cdn,回源使用http。
2. cdn使用公用網域名稱,公用的認證,這樣資源的網域名稱就不能自訂了。回源使用http。
3. 僅提供動態加速,cdn進行tcp代理,不緩衝內容。
4. CloudFlare 提供了Keyless SSL的服務,能夠支援不願意提供私密金鑰,不想使用公用的網域名稱和認證卻又需要使用cdn的網站了。
2.2.3 簡單的大型網站
簡單的定義: 資源只從本站的主域,主域的子域,或者自建/可控的cdn網域名稱載入,幾乎沒有第三方資源。如果網站本身的特性就如此,或願意改造為這樣的類型,部署https就相對容易。Google Twitter都是非常好的範例。優點:已經改成這樣的網站,替換https就比較容易。缺點:如果需要改造,那麼要很大的決心,畢竟幾乎不能使用多樣化的第三方資源了。
2.2.4 複雜,訪問速度重要性稍低的大型網站
複雜的定義:從本站的非主域,或者第三方網站的網域名稱有大量的第三方資源需要載入,多出現在一些平台類,或者有複雜內容展現的的網站。
訪問速度要求:使用者停留時間長或者強需求,使用者對訪問速度的耐受程度較高。比如門戶,視頻,線上交易類(比如火車票 機票 商城)網站。
這樣的網站,可以努力推動所有相關網域名稱升級為支援https。我們用舉例說明下這樣修改會導致一個網站的連結發生怎樣的改變。
負責流量接入的團隊將可控的接入環境改造為http和https都支援,這樣前端工程的工作相對就少一些。大部分時候將連結從http:// 替換為 // 即可. 在主網域名稱是https的情況下,其它資源就能自動從https協議下載入。一些第三方資源怎麼辦?一般來說只有兩種選擇,一遷移到自己的cdn或者idc吧,二強制要求第三方自己能支援https。
以全站https接入的facebook舉例。第三方廠商想在facebook上線一個遊戲。facebook:請提供https接入吧。第三方想:能賺錢啊,還是提供下https接入吧。所以,足夠強勢,有吸引力,合作方也有提供https的能力的話,這是完全可行的。如果你的平台接入的都是一些個人開發人員,而且還賺不到多少錢的情況下,這樣就行不通了。
優點:前端改動相對簡單,不容易出現https下還有http的資源問題。
缺點:通常這樣的實現下,使用者的訪問速度會變慢,比如從2.5秒變為3秒,如上述的理由,使用者還是能接受的。對第三方要求高。
2.2.5 複雜,訪問速度有嚴格要求的大型網站
複雜的定義:同上。
訪問速度要求:停留時間不長,使用者對訪問速度的心理預期較高。
但是如果使用者把網站當作工具使用,需要你很快給出響應的時候,這樣的實現就不好了。後續幾個部分我們介紹下這些最佳化的抉擇。
2.3 網域名稱的選擇
網域名稱對訪問速度的影響具有兩面性:網域名稱多,網域名稱解析和建立串連的時間就多;網域名稱少,下載並發度又不夠。
https下重建串連的時間成本比http更高,對於上面提到的簡單的大型網站,可以只用1-3個網域名稱就能滿足需求,對於百度這樣富展現樣式較多的搜尋引擎來說,頁面可能展示的資源種類太多。而不同類型的資源又是由不同的網域名稱(不同的產品 或者第三方產品)提供的服務,換一個詞搜尋就可能需要重建立立一些資源的ssl連結,會讓使用者感受到卡頓。
如果將網域名稱限制在有限的範圍,維持和這些網域名稱的串連,合并一些資料,加上有spdy,http2.0來保證並發,是可以滿足我們的需求的。
2.4 串連複用
串連複用率可以分為tcp和ssl等不同的層面,需要分開進行分析和統計。
2.5.1 串連複用的意義
HTTP協議(RFC2616)規定一個網域名稱最多不能建立超過2個的TCP串連。但是隨著互連網的發展,一張網頁的元素越來越多,傳輸內容越來越大,一個網域名稱2個串連的限制已經遠遠不能滿足現在網頁載入速度的需求。
目前已經沒有瀏覽器遵守這個規定,各瀏覽器針對單網域名稱建立的TCP串連數如下:
從上表看出,單個網域名稱的串連數基本上是6個。所以只能通過增加網域名稱的方式來增加並發串連數。在HTTP情境下,這樣的方式沒有什麼問題。但是在HTTPS串連下,由於TLS串連建立的成本比較高,增加並發串連數本身就會帶來較大的延遲,所以對網域名稱數需要一個謹慎的控制。
特別是HTTP2即將大規模應用,而HTTP2的最大特性就是多工,使用多個網域名稱和多個串連無法有效發揮多工和壓縮的特性。
那HTTPS協議下,一張網頁到底該有多少網域名稱呢?這個其實沒有定論,取決於網頁需要載入元素個數。
2.4.2 預建串連
既然從協議角度無法減少握手對速度的影響,那能不能提前建立串連,減少使用者可以感知的握手延遲呢?當然是可以的。思路就是預判目前使用者的下一個訪問URL,提前建立串連,當使用者發起真實請求時,TCP及TLS握手都已經完成,只需要在串連上發送應用程式層資料即可。
最簡單有效方式就是在主域下對串連進行預建,可以通過請求一些靜態資源的方式。但是這樣還是不容易做到極致,因為使用哪個串連,並發多少還是瀏覽器控制的。例如你對a網域名稱請求一個圖片,瀏覽器建立了兩個串連,再請求一張圖片的時候,瀏覽器很大機率能夠複用串連,但是當a網域名稱需要載入10個圖片的時候,瀏覽器很可能就會建立串連了。
2.4.3 Spdy的影響
Spdy對於串連複用率的提升非常有效,因為它能支援串連上的並發請求,所以瀏覽器會盡量在這個連結上保持複用。
2.4.4 其它
也可以嘗試一些其他發方法,讓瀏覽器在訪問你的網站之前就建立過https串連,這樣session能夠複用。HSTS也能有效減少跳轉時間,可惜對於複雜的網站來說,開啟需要考慮清楚很多問題。
2.5 最佳化的效果
從百度的最佳化經驗來看看,如果不開啟HSTS,使用者在瀏覽器直接存取主網域名稱,再通過302跳轉到HTTPS。增加的時間平均會有400ms+,其中302跳轉和ssl握手的因素各佔一半。但是對於後續的請求,我們做到了對絕大部分使用者幾乎無感知。
這400ms+還有很多可以最佳化的空間,我們會持續最佳化使用者的體驗。
3 HTTPS遷移遇到的一些常見問題。
3.1 傳遞Referrer
我們可以把自己的網站替換為https,但是一般的網站都有外鏈,要讓外鏈都https目前還不太現實。很多網站需要從referrer中判斷流量來源,因此對於搜尋引擎這樣的網站來說,referer的傳遞還是比較重要的。如果不做任何設定,你會發現在https網站中點擊外鏈並沒有將referrer帶入到http請求的頭部中(http://tools.ietf.org/html/rfc7231#section-5.5.2)。現代的瀏覽器可以用meta標籤來傳遞refer。(http://w3c.github.io/webappsec/specs/referrer-policy)
傳遞完整的url
只傳遞網站,不包含路徑和參數等。
對於不支援meta傳遞referrer的瀏覽器,例如IE8, 我們怎麼辦呢?
可以採用再次跳轉的方法,既然HTTPS下不能給HTTP傳遞referer,我們可以先從HTTPS訪問一個可控的http網站,把需要傳遞的內容放到這個http網站的url中,然後再跳轉到目標地址。
3.2 form提交
有時需要將form提交到第三方網站,而第三方網站又是http的地址,瀏覽器會有不安全的警告。可以和referrer的跳轉傳遞採取相似的邏輯。
但是這樣對referer和form等內容的方案,並不是完美的解決方案,因為這樣還是增加了不安全的因素(劫持,隱私泄露等 )。理想情況需要使用者升級符合最新規範的瀏覽器,以及推進更多的網站遷移至https。
3.3 視頻播放
簡單來說,如果你使用http的協議來播放視頻,那麼瀏覽器仍然會有不安全的提示。所以你有兩種選擇,1讓視頻源提供https。2使用非http的協議,如rtmp協議。
3.4 使用者異常
在https遷移的過程中,也會有不少熱心的使用者向我們反饋遇到的各種問題。
常見的有以下的一些情況:
1. 使用者的系統時間設定錯誤,導致提示認證到期。
2. 使用者使用fiddler等代理進行調試,但是沒有添加這些軟體的根憑證,導致提示認證非法。
3. 使用者使用的Dns為公用dns或者跨網設定dns,一些請求被電訊廠商作為跨網流量攔截。
4. 連通性有問題,我們發現一個小電訊廠商的https失敗率奇高,又沒法聯絡到他們,只能不對他們進行https的轉換。
5. 慢。有時由於網路環境的因素,使用者開啟其他網站也慢,ping哪個網站都要500-2000ms。這時https自然也會很慢。
4 結束語
對於複雜的大型網站來說,HTTPS的部署有很多工作要完成。
面對困難和挑戰,有充足的動力支援著我們前進:https上線後,劫持等原因導致的使用者功能異常,隱私泄露的反饋大幅減少。
熱心的使用者經常會向我們反饋遇到的各種問題。在以前,有時即使我們確定了是劫持的問題,能夠解決問題的方法也非常有限。每當這種時候,自己總會產生一些無力感。
HTTPS的全站部署,給我們提供了能解決大部分問題的選項。能讓一個做技術的人看到自己的努力解決了使用者的問題,這就是最棒的收穫。
HTTPS沒有想像中難用和可怕,只是沒有經過最佳化。與大家共勉。