Stack Overflow 是世界上最大的問答網站,大部分碼農必須依賴它,最近他們也全部遷移到了 HTTPS,寫了一篇很長的文章來說這個事情,非常具有實踐性,這篇文章沒有講 HTTPS 的概念,而是強調他們是如何進行遷移的。從一篇博文的角度來說,很難優雅的描述這么復雜的一個事情,他們從三個部分來描述這個事情,分別是基礎架構、應用層代碼、犯過的一些錯誤。
面臨的挑戰
Stack Overflow 從 2013 年就開始考慮遷移到 HTTPS 了,也就是說這個項目從準備到上線一共花了四年的事情,原因在哪兒呢?第一點是項目優先級的問題,Stack Overflow 不是電商系統也不是銀行系統,它的數據都可以任意下載,所以安全并不是最重要的事情,相對其他工作來說,優先級低了不少。
第二點原因就是這個工作非常復雜,有很多的依賴性。有上百個域名,而且還有四級域名(比如 meta.gaming.stackexchange.com);允許用戶上傳內容(比如嵌入 YouTube 的視頻);只有一個數據中心(需要考慮延遲的問題);有廣告(這些廣告本身可能沒有 HTTPS);有很多 HTTP 的 API(很難讓用戶遷移);用了 websockets,對性能要求很高,支持 HTTPS 可能會讓性能受損。
最終他們還是選擇支持 HTTPS 了,原因在于有很多不同的角色在 Stack Overflow 上工作,需要保證他們的安全、隱私不被泄漏;另外 Google 搜索排名會優先對待 HTTPS 協議的網站(雖然不知道具體的影響是什么);最重要的遷移原因居然是性能(想想 HTTP/2)。
復雜的證書
Stack Overflow 為什么不使用 Let’s Encrypt 呢,因為 Let’s Encrypt 適合單個域名或幾個域名的網站,而且它也不支持通配符的證書。而想下 Stack Overflow,每新增一個問答類別,就會增加一個域名。域名有上百個,更可怕的是還有四級域名。
假如不把所有域名證書進行合并,那么每部署一個問答類別,就要新弄一個證書,另外居然有 2% 的客戶不支持 SNI(潛臺詞這些用戶不能丟失,不能丟失就只能為新增的網站部署新的 IP 地址),SNI 是什么就不解釋了,假如你使用虛擬主機(IP 資源有限),那么有些用戶可能就永不了你的 HTTPS 網站了。
另外不使用 Let’s Encrypt 的原因就是希望自己控制證書,因為需要將證書部署在負載均衡設備上(Stack Overflow 用的是 HAproxy)、CDN/proxy 上;未來 Stack Overflow 可能還會支持 HPKP。
最終他們選擇了 DigiCert 的證書,可以稱之為多域名統配型證書,大部分域名使用同一個證書,原因上面也講了,為了管理方便,并且為了兼容不支持 SNI 的用戶。
什么是多域名通配型證書?他們有很多一級子域名,比如 askubuntu.com 和 serverfault.com,為了放在一個證書中,就必須選擇多域名證書;而很多一級子域名又可能有很多子域名,為了方便可以選擇通配型證書,比如一個證書可以支持 boardgames.stackexchange.com 和 gaming.stackexchange.com(可能還有其他的)。
比較麻煩的是他們居然有四級域名(他們稱為 second place 服務),比如有 meta.gaming.stackexchange.com、meta.boardgames.stackexchange.com 等等,而通配符只能允許“通配”一級,比如不允許meta.*.stackexchange.com
通配符。最終的方案就是統一規劃 second place,選擇標準的域名 *.meta.stackexchange.com
來提供服務,假如是老的訪問方式就進行 301 跳轉,你可以訪問 http://meta.gaming.stackexchange.com 看看。
在這個遷移改造中也引出一個新問題,在登錄的時候是給頂級域名種植 Cookie,它的子域名會繼承,這樣是有一些風險的,所以需要移除一些域名,舉個例子,他們是使用 SendGrid 發送郵件的,里面的引用地址都是假如是 sg-links.stackoverflow.com(CNAME 到 SendGrid),那么就會發送 stackoverflow.com 的 Cookie 到 SendGrid 上,帶來了安全問題。所以剝離 sg-links.stackoverflow.com 域名至 sg-links.stackoverflow.email
另外用戶可能很奇怪 Stack Overflow 的官方博客為什么使用 stackoverflow.blog 域名,道理很簡單,就是為了避免 Cookie 帶來的安全問題,另外官方博客使用的證書也是獨立的,使用的是 Let’s Encrypt 的證書,這是很好的一個思路。
HTTP/2
在傳統的觀點中,HTTPS 是影響性能的,但是這個觀點已經過時了,大部分網站支持 HTTPS 的下一步就是支持 HTTP/2。其實 HTTP/2 協議本身是不需要加密的,但是很多瀏覽器為了實現一些功能,需要一條加密的通道,所以演變成 HTTP/2 必須構建在 HTTPS 上面。
HTTP/2 在性能上有很多優勢,其中有個特性和證書有關,就是 HTTP/2 能夠推送內容到不同的域名,只要證書符合兩個條件:
- 這些域名最終能夠解析到同一個 IP 地址
- 這些不同的域名有相同的證書
而 stackoverflow.com 和 cdn.sstatic.net(自己的 CDN?怎么能解析到相同的地址) 解析的 IP 地址是相同的,證書也是同一個,這將為將來實現 push 打下了基礎。另外強調一句,Stack Overflow 的遷移動力永遠是性能而非安全性。
HAproxy
選擇 HAproxy 作為負載均衡而非 Nginx 就因為其簡單、專注。HAProxy 1.5 版本后支持 OpenSSL,一些配置說明如下:
- 運行的是 4 顆 CPU,其中一個處理到后端的請求,另外的處理 HTTPS 的邏輯
- HAproxy 通過 abstract named socket 連接后端
- 轉發到后端的時候,會通過一個 Header 頭來標識是不是 HTTPS 的請求
- 選擇的密鑰套件標準來之 Mozilla
CDN/Proxy
對于一個大型網站來說,服務器不是第一位的,重要的是解決延遲問題,但這受限與光速,而云技術能夠改善,這時候 CDN 可以出場了,Stack Overflow 只有一個數據中心,所以很迫切使用 CDN,尤其在部署 HTTPS 以后,需要通過減少延遲來抵消 HTTPS 帶來的性能消耗(主要是握手協議)。
Stack Overflow 混用了 Cloudflare 和 Fastly 這兩個 CDN。CDN 的功能主要就是處理 HTTPS 的握手、防止 DDoS 攻擊、一些 CDN 功能、離用戶最近。
個人沒有使用 CDN 的經驗,包括 Global DNS 的概念,所以這里沒有看的太明白,最終選擇 Fastly 的原因在于他們的服務比較適合,比較定制化,尤其 Varish 的 VCL 配置非常的靈活。當然不是說 Cloudflare 不好,他自己的博客就使用了 Cloudflare 的 CDN。
另外自己未來也要了解下負載均衡和 CDN 的相關知識,這也和 Stack Overflow 的架構有關,這也要看下其他相關的博客文章。
全局登錄
雖然 Stack Overflow 有很多域名,但是他是中心化的服務,不管你訪問那個域名,最終執行的就是 w3wp.exe 這個進程,通過不同的 Host Header 來進行不同的處理。簡單的說所有的網站可以運行在一個服務器上,能夠快速的進行擴展。
全局登錄從應用層角度來說是非常重要的事情,所以優先提一下,在早期的 Stack Overflow 中,不同的網站種植不同域名的 Cookie,對于用戶來說這個體驗很不好,你本質上是一個網站,在不同的問答區切換卻要登錄多次。
他們通過全局登錄解決了這個問題(其實和 HTTPS 沒有啥關系,只是在遷移過程中,重新規劃了域名體系),全局登錄的技術方案其實就是在用戶從某個問答切換到另外個問答的時候,執行一個圖片加載操作,進行 Token 的校驗和 Cookie 的處理。
HTTPS 開發環境
從我個人角度來說,當部署 HTTPS 后,本地開發環境是否也要部署 HTTPS 呢,這還有挑戰性,因為你假如在本地全面部署 HTTPS (尤其將證書放在本地,這很有風險),Stack Overflow 是如何做的?
Stack Overflow 的開發環境是使用的 IIS,它有個工具能夠很好的部署本地開發環境。本地開發環境用的域名和證書都和線上是不一樣的(有可能自建證書)。
另外在本地開發環境盡量保持和線上環境是一致的,舉兩個例子,第一不建議使用 /content 加載資源,因為這會隱藏一些線上會遇到的問題(比如 CORS),大部分人喜歡在本地搭建完整的環境(比如線上有 5 個域名,而開發環境只有 1 個域名),讓自己的開發環境部署在一臺機器上是個很不好的實踐。
第二個例子就是假如開發環境不使用 HTTPS ,可能會遇到 HTTPS 降級的問題,比如不發送 referer 。
來自用戶的混合內容
HTTPS 協議要求加載的資源必須全部是 HTTPS 協議的,不建議加載 HTTP 協議的資源,但是 Stack Overflow 允許用戶提交自己的內容(比如圖片和視頻),他們是如何解決這問題的?
其實處理策略很簡單,就是用戶最新提交的內容必須是 HTTPS,堵住了新增內容就要處理歷史數據了。這里通過圖片來說明下。他們發現 90%+ 的圖片存儲在 stack.imgur.com 上了,這個轉換工作很簡單,不用做任何的數據分析。然后他們分析了其他內容,發現很多引入的資源本身就直接支持 HTTPS(還是國外走的比較前),基本的處理規則就是假如外部資源直接支持 HTTPS 就使用,假如不支持 HTTPS 就進行轉換(不是數據層的轉換)。
為什么 Stack Overflow 不通過 Porxy 來統一處理圖片,主要是帶寬和存儲的問題,另外圖片也有版權問題,不應該通過 Proxy 轉換,這涉及到版權問題。另外很多老帖子其實不是經常訪問,即使加載不了 HTTPS 的資源,也沒有多大的問題。
301 問題
301 可能會影響 Google 收集,所以務必注意 canonical 指令,第二個問題就是注意 301 版本的問題,不能什么情況下都進行 301 跳轉,比如 POST 請求不能跳轉,作者寫了個處理 301 規則的偽代碼,可以借鑒下。
Websockets
在 Stack Overflow 上很多地方用到了 Websockets,比如通知類信息,支持 SSL 也很簡單,就是將 ws:// 協議變更為 wss:// 協議,假如不轉換可能也有混合內容的問題,另外老的代理處理 ws:// 協議有些問題。
另外 websockets 非常消耗內存,尤其為了支持 TLS 恢復功能(減少兩次握手),用在 TLS 會話緩存占用內存非常大。
一些錯誤
(1)不要使用 Protocol-Relative URLs
很多開發者在支持 HTTP 和 HTTPS 的時候,為了引入元素(比如圖片),喜歡使用 //example.com 這樣的方式,但是 Stack Overflow 認為是錯誤的(至少這個觀點適合他們),因為不僅僅是頁面會引入圖片,在郵件和 API 中也會引入圖片資源,但是他們不會有效執行 //example.com 這樣的方式,所以 Stack Overflow 建議使用絕對路徑,他們通過兩個全局變量來實現。
(2)301 Caching
可能會有死循環問題,需要注意。
最后
- TLS 1.0, 1.1, 1.2 協議支持,未來會支持 TLS 1.3 ,另外也即將廢棄 TLS 1.0
- 不支持 SSL v2, v3,因為有安全問題
- 選擇什么密鑰套件,對于 CDN 使用 Fastly’s default suite,對于負載均衡使用 Mozilla’s Modern compatibility suite
- CDN 到后端也是使用 HTTPS 協議(真安全)
- 支持前向加密
- 不支持 HPKP
- 不支持 SNI,為了 HTTP/2 的性能
- 不支持 IE6,因為不支持 SSL 協議,大部分碼農也不使用 IE6