大型網站的 HTTPS 實踐(四)——協議層以外的實踐

baidu發表於2015-05-07

 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 更高,對於上面提到的簡單的大型站點, 可以用少量域名就能滿足需求,對於百度這樣富展現樣式較多的搜尋引擎來說,頁面可能展示的資源種類太多。而不同型別的資源又是由不同的域名 (不同的產品 或者第三方產品) 提供的服務,換一個詞搜尋就可能需要重新建立一些資源的 ssl 連結,會讓使用者感受到卡頓。

  如果將域名限制在有限的範圍,維持和這些域名的連線,合併一些資料,加上有 spdy,http2.0 來保證併發,是可以滿足我們的需求的。

  2.4 連線複用

  連線複用率可以分為 tcp 和 ssl 等不同的層面,需要分開進行分析和統計。

  2.4.1 連線複用的意義

  HTTP 協議 (RFC2616) 規定一個域名最多不能建立超過 2 個的 TCP 連線。但是隨著網際網路的發展,一張網頁的元素越來越多,傳輸內容越來越大,一個域名 2 個連線的限制已經遠遠不能滿足現在網頁載入速度的需求。

  目前已經沒有瀏覽器遵守這個規定,各瀏覽器針對單域名建立的 TCP 連線數如下:

瀏覽器

連線數

Firefox 2

2
Firefox 3+

6

Chrome

6

Ie10

8
IE8

6

Safari 5

6
Opera 12

6

  表格 1 瀏覽器單域名建立的最大併發連線數

  從上表看出,單個域名的連線數基本上是 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)

  <meta name=”referrer” content=”always”> 傳遞完整的 url

  <meta name=”referrer” content=”origin”> 只傳遞站點,不包含路徑和引數等。

  對於不支援 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 沒有想像中難用和可怕,只是沒有經過優化。與大家共勉。

  相關文件:

  大型網站的 HTTPS 實踐(一)——HTTPS 對效能的影響

  大型網站的 HTTPS 實踐(二)——HTTPS 對效能的影響

  大型網站的 HTTPS 實踐(三)——基於協議和配置的優化

  大型網站的 HTTPS 實踐(四)——協議層以外的實踐

相關文章