1. 程式人生 > >為什麼我們應該儘快支援 ALPN?

為什麼我們應該儘快支援 ALPN?

提醒:本文最後更新於 916 天前,文中所描述的資訊可能已發生改變,請謹慎使用。

先來回顧一下,瀏覽器在訪問 HTTPS 網站時,如何得知服務端是否支援 HTTP/2?答案是藉助 HTTP/2 的協議協商機制:在 HTTP/2 Over HTTP 中,可以使用 HTTP 的 Upgrade 機制進行協商;而對於 HTTP/2 Over TLS,可以使用 TLS 的 NPN 或 ALPN 擴充套件來完成協商。HTTP/2 的這兩種協商方式,不瞭解的同學請看《談談 HTTP/2 的協議協商機制》,這裡不再贅述。

當前所有瀏覽器,都只支援 HTTP/2 Over TLS。也就是說,瀏覽器和服務端都支援 NPN 或 ALPN 協商,是用上 HTTP/2 的大前提

。本文重點討論 NPN 和 ALPN。

NPN(Next Protocol Negotiation,下一代協議協商),是一個 TLS 擴充套件,由 Google 在開發 SPDY 協議時提出。隨著 SPDY 被 HTTP/2 取代,NPN 也被修訂為 ALPN(Application Layer Protocol Negotiation,應用層協議協商)。二者目標一致,但實現細節不一樣,相互不相容。以下是它們主要差別:

  • NPN 是服務端傳送所支援的 HTTP 協議列表,由客戶端選擇;而 ALPN 是客戶端傳送所支援的 HTTP 協議列表,由服務端選擇;
  • NPN 的協商結果是在 Change Cipher Spec 之後加密傳送給服務端;而 ALPN 的協商結果是通過 Server Hello 明文發給客戶端;

大部分 Web Server 都依賴 OpenSSL 庫提供 HTTPS 服務,對於它們來說,是否支援 NPN 或 ALPN 完全取決於使用的 OpenSSL 版本。通常,如果在編譯時不特意指定 OpenSSL 目錄,Web Server 會使用作業系統內建的 OpenSSL 庫。

OpenSSL 1.0.2 才開始支援 ALPN,當前主流伺服器作業系統基本都沒有內建這個版本。以下是一份粗略的統計(via):

作業系統 內建 OpenSSL 版本
CentOS 5 0.9.8e
CentOS 6 1.0.1e
CentOS 7 1.0.1e
Ubuntu 14.04 LTS 1.0.1f
Ubuntu 16.04 LTS 1.0.2g
Debian 7 (Wheezy) 1.0.1e
Debian 8 (Jessie) 1.0.1k

這份表格列舉的系統中,只有最近剛釋出的 Ubuntu 16.04 才內建了支援 ALPN 的 OpenSSL 1.0.2。也就是說,直接使用系統 OpenSSL 庫的 Web Server,極有可能不支援 ALPN 擴充套件。

去年,Google 在 Chrome 47 中移除了對 NPN 的支援,只支援 ALPN,但很快就引發一大批 HTTP/2 網站的抱怨。最終,Google 不得不在接下來的版本中又重新啟用了 NPN

半年後的今天,Google 又一次決定在 Chrome 51 中移除 NPN,預計 5 月底釋出。這一次恐怕不會再反悔了。

本文開頭那篇文章的作者悲觀地認為:現在 OpenSSL 1.0.2 的普及程度仍然太低,Chrome 現在去掉對 NPN 的支援,仍然會導致一大批不支援 ALPN 的 HTTP/2 網站最終無法協商到 HTTP/2,只能降級使用 HTTP/1.1。

對此,我的觀點是,該來的總會來,既然不能改變結果,不如早做準備。

通過 OpenSSL 命令列工具,可以快速檢視自己的 HTTP/2 服務是否支援 ALPN 擴充套件:

openssl s_client -alpn h2 -servername imququ.com -connect imququ.com:443 < /dev/null | grep 'ALPN'

如果提示 unknown option -alpn,說明本地的 OpenSSL 版本太低(可通過 openssl version 檢視),請升級到 1.0.2+。如果不方便升級,也可以使用 Qualys SSL Labs's SSL Server Test 這個線上工具來測試。

如果結果包含 ALPN protocol: h2,說明服務端支援 ALPN,不受 Chrome 51+ 去掉 NPN 的影響。

如果結果包含 No ALPN negotiated,說明服務端不支援 ALPN,在 Chrome 51+ 中無法協商到 HTTP/2,需要儘快升級。

由於 OpenSSL 是系統基礎庫,大量其他軟體都對它有依賴,如果直接升級系統自帶的 OpenSSL,很容易引發各種問題。更為穩妥的做法是在編譯 Web Server 時自己指定 OpenSSL 的位置。例如,我在《本部落格 Nginx 配置之完整篇》這篇文章中提供的 Nginx 編譯步驟中,就通過 --with-openssl 指定了新版 OpenSSL 原始碼路徑,這樣編譯出來的 Nginx 就會用上最新的 OpenSSL 庫。

如果你在用 LibreSSL 做為 Web Server 的 SSL 庫,需要升級到 2.1.3+ 才支援 ALPN。

OkHttp 是一個 Android 下比較常用的 HTTP 客戶端,支援 HTTP 和 HTTP/2 協議。它在一年前也移除了對 NPN 的支援,所以支援 HTTP/2 但不支援 ALPN 的網站,在 OkHttp 客戶端中會降級到 HTTP/1.1。

Chrome 51 這次也會徹底移除對 SPDY 的支援,如果你的網站還只支援 SPDY,趁這個機會直接升級到 HTTP/2 + ALPN 吧。

--EOF--

提醒:本文最後更新於 916 天前,文中所描述的資訊可能已發生改變,請謹慎使用。