Server Name Indication (SNI) 原理簡介

Chen Li-Chi (陳立其)
4 min readSep 12, 2020

如果你有接觸過 web server ,例如 apache 或是 nginx 或是 IIS,有個名詞叫做 virtual host 。意思是你可以在一台 web server 上面裝多個網站,可以讓 a.comb.com 對應到不同的處理邏輯。

HTTP headers 裡面有個欄位叫做 Host ,會帶有 client 想要訪問的網站域名。 Server 可以根據這個訊息來判斷 client 倒底想訪問 a.com 還是 b.com

HTTPS 造成的問題

HTTPS 把這件事變得有點麻煩。在 TCP 連線建立完成後,接著進行的是 TLS handshake ,這時候 Server 會需要回應一張證書給 client 。如果今天一個網站有一張以上的證書,事情就變得很麻煩,我到底要給哪一張。

這時候沒辦法看 Host header ,原因是 TLS 會發生在 HTTP headers 訊息傳送之前。除非你可以預知未來,不然無法偷看到 Host header 。

因此 SNI 要解決的問題,就是 server 不知道要給哪一張 certificate 的問題。

SNI extension

SNI 是在 TLS handshake 的 client hello 規格部分加一個額外的欄位,裡面放的是 client 想訪問的域名。如此一來 server 就知道要回應哪一張證書了。

為什麼不把域名全部放在同一張證書就好

這個其實也是可以的。SAN (Subject Alternate Name) 可以放多個域名或是 ip,但會有維護上的問題。假設未來多了一個網站,就要更新一次證書,這也是很繁瑣的。

這樣還安全嗎

答案是,看情況。TLS handshake 做完之後才會加密,這等於說你要訪問哪個站其實是可以被中間的網路節點看到的。因為 SNI 露在外面,有些國家網路政策可以利用這個資訊,在網路中間網路節點作怪,故意不給你訪問某些網站。

SNI 跟 Host header 可不可以不同?

理論上可以,但有機率會被 CDN 當作非法的使用者而被擋下來,例如 Amazon CloudFront 就會擋下來。

這種情形稱作 domain fronting 。

用 openssl 指令做個實驗

openssl s_client -connect cube.lichi-chen.com:443 -servername cube.lichi-chen.com

這個指令會建立 TLS handshake 。建立好了我們就接著使用 openssl 傳送 HTTP 的訊息。

GET / HTTP/1.1
Host: cube.lichi-chen.com

記得最後一行要多加個換行才會送出喔。這時候會拿到成功的回應。

故意把 SNI 跟 Host header 弄成不同

舉例來說,如果 CloudFront (AWS 旗下的 CDN 服務) 認出這個 host 跟你的 SNI 所屬的網站是不同的屬於不同 AWS 帳戶下的資源。 CloudFront 會給你 421 錯誤訊息。

GET / HTTP/1.1
Host: www.alexa.com
HTTP/1.1 421 Misdirected Request
Server: CloudFront
Date: Sat, 08 Aug 2020 10:12:53 GMT
Content-Type: text/html
Content-Length: 187
Connection: keep-alive
X-Cache: Error from cloudfront
Via: 1.1 839de761badea2aa0a28c5970b81514d.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: NRT12-C4
X-Amz-Cf-Id: G0WiLPYz2LD0zZ57GcN6Vl8Ym86z4HuOrd8QeOThBIVba-Gifywtxg==
<html>
<head><title>421 Misdirected Request</title></head>
<body bgcolor="white">
<center><h1>421 Misdirected Request</h1></center>
<hr><center>CloudFront</center>
</body>
</html>

--

--