<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>通訊協定 on Wayne's blog | 偉恩的部落格 | 技術博客</title><link>https://blog.wayne-project.com/categories/communications-protocol/</link><description>Recent content in 通訊協定 on Wayne's blog | 偉恩的部落格 | 技術博客</description><generator>Hugo -- gohugo.io</generator><language>zh-tw</language><lastBuildDate>Sat, 28 Jan 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.wayne-project.com/categories/communications-protocol/index.xml" rel="self" type="application/rss+xml"/><item><title>關於 TCP 的三次握手與四次揮手，滿分的回答在此！</title><link>https://blog.wayne-project.com/2023-01-28/tcp-three-way-four-way-handshake/</link><pubDate>Sat, 28 Jan 2023 00:00:00 +0000</pubDate><guid>https://blog.wayne-project.com/2023-01-28/tcp-three-way-four-way-handshake/</guid><description>&lt;img src="https://live.staticflickr.com/65535/52676765170_50b661b18d_o.jpg" alt="Featured image of post 關於 TCP 的三次握手與四次揮手，滿分的回答在此！" /&gt;&lt;style&gt;
.article-content p code {
background-color: #f5f5f5;
color: #ff3860;
}
.focus {
background: #f1e2e2;
color: #d62c2c;
padding: 0 5px;
}
.text-center {
text-align: center !important;
}
&lt;/style&gt;
&lt;p&gt;&lt;a class="link" href="http://www.52im.net/thread-1729-1-1.html" target="_blank" rel="noopener"
&gt;參考網站&lt;/a&gt;&lt;br&gt;
&lt;a class="link" href="http://www.52im.net/thread-258-1-1.html" target="_blank" rel="noopener"
&gt;參考網站&lt;/a&gt;&lt;br&gt;
&lt;a class="link" href="http://www.52im.net/thread-275-1-1.html" target="_blank" rel="noopener"
&gt;參考網站&lt;/a&gt;&lt;br&gt;
&lt;a class="link" href="https://segmentfault.com/a/1190000039165592" target="_blank" rel="noopener"
&gt;參考網站&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;在面試中，計算機網絡的 TCP 三次握手和四次揮手是很常見的問題，但是在實際面試中，面試官會更願意聽到怎樣的回答呢？詳細程度是怎樣的？&lt;/p&gt;
&lt;p&gt;越簡單常見的問題越不可小覷，萬丈高樓平地起，把簡單的問題深入化，才更能拉開與競爭者的距離。掌握了本文講的全部知識點，關於 TCP 三次握手和四次揮手基本就OK 了😊&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="tcp-與-udp"&gt;TCP 與 UDP&lt;/h2&gt;
&lt;p&gt;講解 TCP 三次握手和四次握手之前，我們先了解一下 TCP 和 UDP 這兩個重量級的傳輸層協議。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;💦 用戶數據報協議 &lt;strong&gt;UDP&lt;/strong&gt;（User Datagram Protocol）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UDP 在傳送數據之前不需要先建立連接，遠程主機在收到 UDP 報文後，不需要給出任何確認。&lt;/li&gt;
&lt;li&gt;雖然 UDP &lt;strong&gt;不提供可靠交付&lt;/strong&gt;，但在某些情況下 UDP 確是一種最有效的工作方式（一般用於&lt;strong&gt;即時通信&lt;/strong&gt;、&lt;strong&gt;直播串流&lt;/strong&gt;），比如：QQ 語音、QQ 視頻、直播等等。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;💦 傳輸控制協議 &lt;strong&gt;TCP&lt;/strong&gt;（Transmission Control Protocol）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TCP 提供面向連接的服務。在傳送數據之前必須先建立連接，數據傳送結束後要釋放連接。&lt;/li&gt;
&lt;li&gt;TCP 不提供廣播或多播服務。由於 TCP 要提供&lt;strong&gt;可靠&lt;/strong&gt;的，面向連接的傳輸服務（TCP 的可靠，體現在 TCP 在傳遞數據之前，會有&lt;strong&gt;三次握手&lt;/strong&gt;來建立連接，而且在數據傳遞時，有確認、窗口、重傳、流量控制、擁塞控制機制，在數據傳完後，還會&lt;strong&gt;四次揮手&lt;/strong&gt;斷開連接用來節約系統資源），這不僅使協議數據單元的首部增大很多，還要佔用許多處理機資源。&lt;/li&gt;
&lt;li&gt;TCP 一般用於&lt;strong&gt;文件傳輸&lt;/strong&gt;、&lt;strong&gt;發送和接收郵件&lt;/strong&gt;、&lt;strong&gt;遠程登錄&lt;/strong&gt;等場景。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="tcp-報文段首部格式"&gt;TCP 報文段首部格式&lt;/h2&gt;
&lt;p&gt;TCP 報文段的具體格式大家可以不必都記住，但是其中的幾個控制位與我們接下來要講的三次握手和四次揮手息息相關，大家一定要牢記。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://live.staticflickr.com/65535/52676335786_d3e84c672f_o.png"
max-width="100%"
max-height="100%"
class="gallery-image"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;首部固定部分各字段意義如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;源端口和目的端口&lt;/strong&gt;： 各佔 2 個字節，分別寫入源端口和目的端口。&lt;code&gt;IP 地址&lt;/code&gt; + &lt;code&gt;端口號&lt;/code&gt;就可以確定一個進程地址。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;序號/序列號&lt;/strong&gt;（Sequense Number，SN）： 在一個TCP 連接中傳送的字節流中的每一個字節都按順序編號。該字段表示本報文段所發送的數據的第一個字節的序號。&lt;strong&gt;初始序號稱為Init Sequense Number&lt;/strong&gt;，&lt;strong&gt;ISN&lt;/strong&gt;（序號/序列號這個字段很重要，大家留個印象，下文會詳細講解）&lt;br&gt;
例如，一報文段的序號是 101，共有 100 字節的數據。這就表明：本報文段的數據的第一個字節的序號是 101，最後一個字節的序號是 200。顯然，下一個報文段的數據序號應當從 201 開始，即下一個報文段的序號字段值應為 201。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;確認號 ack&lt;/strong&gt;： 期望收到對方下一個報文段的第一個數據字節的序號。若確認號為 N，則表明：到序號 N-1 為止的所有數據都已正確收到。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;數據偏移&lt;/strong&gt;（首部長度）： 它指出 TCP 報文段的數據起始處距離 TCP 報文段的起始處有多遠。這個字段實際上是指出 TCP 報文段的首部長度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保留&lt;/strong&gt;： 佔 6 位，應置為 0，保留為今後使用。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;⭐ 大家看上圖，保留位的右邊還有 6 個控制位（重要），這是 TCP 用來說明該報文段性質的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;緊急位URG&lt;/strong&gt;： 當 URG = 1 時，表明此報文段中有緊急數據，是高優先級的數據，應盡快發送，不用在緩存中排隊。該控制位需配合緊急指針使用（緊急指針指出本報文段中緊急數據的字節數）&lt;br&gt;
舉個例子：我們需要取消一個已經發送了很長程序的運行，因此用戶從鍵盤發出中斷命令。如果不使用緊急數據，那麼這個指令將存儲在接收 TCP 的緩存末尾，只有在所有的數據被處理完畢後這兩個字符才被交付接收方的應用進程，這樣做就無法實現立即中斷。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;確認ACK&lt;/strong&gt;： 僅當 ACK = 1 時確認號字段才有效，當 ACK = 0 時確認號無效。TCP 規定，在連接建立後所有傳送的報文段都必須把 ACK 置為 1。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;推送PSH&lt;/strong&gt;： 當兩個應用進程進行交互式的通信時，有時在一端的應用進程希望在鍵入一個命令後立即就能收到對方的響應。在這種情況下，TCP 就可以使用推送（push）操作。這時，發送方 TCP 把 PSH 置為 1，並立即創建一個報文段發送出去。接收方 TCP 收到 PSH = 1 的報文段，就盡快地交付接收應用進程。而不用等到整個緩存都填滿了後再向上交付。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;復位RST&lt;/strong&gt;： 當 RST = 1 時，表明TCP 連接中出現了嚴重錯誤（如由於主機崩潰或其他原因），必須釋放連接，然後再重新建立傳輸連接。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;同步SYN&lt;/strong&gt;： SYN = 1 表示這是一個連接請求或連接接受報文。&lt;br&gt;
當 SYN = 1 而 ACK = 0 時，表明這是一個連接請求報文段。對方若同意建立連接，則應在響應的報文段中使 SYN = 1 且 ACK = 1。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;終止FIN&lt;/strong&gt;： 用來釋放一個連接。當 FIN = 1 時，表明此報文段的發送發的數據已發送完畢，並要求釋放運輸連接。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="tcp-三次握手建立連接"&gt;TCP 三次握手建立連接&lt;/h2&gt;
&lt;h3 id="三次握手過程詳解"&gt;三次握手過程詳解&lt;/h3&gt;
&lt;p&gt;三次握手的原文是 &lt;code&gt;three-way handshake&lt;/code&gt;，整個名詞的可以翻譯為：&lt;strong&gt;需要三個步驟才能建立握手/連接的機制&lt;/strong&gt;。當然，三次握手也可以叫 &lt;code&gt;three-message handshake&lt;/code&gt;，通過三條消息來建立的握手/連接。&lt;/p&gt;
&lt;p&gt;進行三次握手的主要作用就是為了確認雙方的接收能力和發送能力是否正常、指定自己的 &lt;strong&gt;初始化序列號 (Init Sequense Number，&lt;/strong&gt;&lt;code&gt;ISN&lt;/code&gt; &lt;strong&gt;)&lt;/strong&gt; 為後面的可靠性傳輸做準備。&lt;/p&gt;
&lt;p&gt;三次握手過程如下圖：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://live.staticflickr.com/65535/52676335796_725180a8d9_o.png"
max-width="100%"
max-height="100%"
class="gallery-image"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;回顧一下圖中字符的含義：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SYN&lt;/strong&gt;： 連接請求/接收報文段&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;seq&lt;/strong&gt;： 發送的第一個字節的序號&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ACK&lt;/strong&gt;： 確認報文段&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ack&lt;/strong&gt;： 確認號。希望收到的下一個數據的第一個字節的序號&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;剛開始客戶端處於&lt;/strong&gt; &lt;code&gt;Closed&lt;/code&gt; &lt;strong&gt;的狀態，而服務端處於&lt;/strong&gt; &lt;code&gt;Listen&lt;/code&gt; &lt;strong&gt;狀態：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;CLOSED&lt;/code&gt;： 沒有任何連接狀態&lt;br&gt;
&lt;code&gt;LISTEN&lt;/code&gt;： 偵聽來自遠方 TCP 端口的連接請求&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;第一次握手&lt;/strong&gt;： 客戶端向服務端發送一個 SYN 報文（SYN = 1），並指明客戶端的初始化序列號 ISN(x)，即圖中的 seq = x，表示本報文段所發送的數據的第一個字節的序號。此時客戶端處於 &lt;code&gt;SYN_Send&lt;/code&gt; 狀態。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SYN-SENT&lt;/strong&gt;： 在發送連接請求後等待匹配的連接請求&lt;/p&gt;&lt;/blockquote&gt;
&lt;ol start="2"&gt;
&lt;li&gt;&lt;strong&gt;第二次握手&lt;/strong&gt;： 服務器收到客戶端的 SYN 報文之後，會發送 SYN 報文作為應答（SYN = 1），並且指定自己的初始化序列號 ISN(y)，即圖中的 seq = y。同時會把客戶端的 ISN + 1 作為確認號 ack 的值，表示已經收到了客戶端發來的的 SYN 報文，希望收到的下一個數據的第一個字節的序號是 x + 1，此時服務器處於 &lt;code&gt;SYN_REVD&lt;/code&gt; 的狀態。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SYN-RECEIVED&lt;/strong&gt;： 在收到和發送一個連接請求後等待對連接請求的確認&lt;/p&gt;&lt;/blockquote&gt;
&lt;ol start="3"&gt;
&lt;li&gt;&lt;strong&gt;第三次握手&lt;/strong&gt;： 客戶端收到服務器端響應的 SYN 報文之後，會發送一個 ACK 報文，也是一樣把服務器的 ISN + 1 作為ack 的值，表示已經收到了服務端發來的的 SYN 報文，希望收到的下一個數據的第一個字節的序號是 y + 1，並指明此時客戶端的序列號 seq = x + 1（初始為 seq = x，所以第二個報文段要 +1），此時客戶端處於 &lt;code&gt;Establised&lt;/code&gt; 狀態。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;服務器收到ACK 報文之後，也處於 &lt;code&gt;Establised 狀態&lt;/code&gt;，至此，雙方建立起了 TCP 連接。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ESTABLISHED&lt;/strong&gt;： 代表一個打開的連接，數據可以傳送給用戶&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id="為什麼要三次握手"&gt;為什麼要三次握手&lt;/h3&gt;
&lt;p&gt;三次握手的目的是建立可靠的通信信道，說到通訊，簡單來說就是數據的發送與接收，而三次握手最主要的目的就是&lt;strong&gt;雙方確認自己與對方的發送與接收是正常的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;只有經過三次握手才能確認雙發的收發功能都正常，缺一不可：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一次握手（客戶端發送SYN 報文給服務器，服務器接收該報文）：&lt;br&gt;
客戶端什麼都不能確認；服務器確認了對方發送正常，自己接收正常。&lt;/li&gt;
&lt;li&gt;第二次握手（服務器響應SYN 報文給客戶端，客戶端接收該報文）：&lt;br&gt;
客戶端確認了：自己發送、接收正常，對方發送、接收正常；&lt;br&gt;
服務器確認了：對方發送正常，自己接收正常。&lt;/li&gt;
&lt;li&gt;第三次握手（客戶端發送ACK 報文給服務器）：&lt;br&gt;
客戶端確認了：自己發送、接收正常，對方發送、接收正常；&lt;br&gt;
服務器確認了：自己發送、接收正常，對方發送、接收正常。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="isn-initial-sequence-number-是固定的嗎"&gt;ISN (Initial Sequence Number) 是固定的嗎？&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;三次握手的其中一個重要功能是客戶端和服務端交換 ISN(Initial Sequence Number)，以便讓對方知道接下來接收數據的時候如何按序列號組裝數據。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;當一端為建立連接而發送它的 SYN 時，它會為連接選擇一個初始序號。ISN 隨時間而變化，因此每個連接都將具有不同的 ISN。&lt;strong&gt;如果 ISN 是固定的，攻擊者很容易猜出後續的確認號，因此 ISN 是動態生成的。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="三次握手過程中可以攜帶數據嗎"&gt;三次握手過程中可以攜帶數據嗎？&lt;/h3&gt;
&lt;p&gt;第三次握手的時候，是可以攜帶數據的。但是，&lt;strong&gt;第一次、第二次握手絕對不可以攜帶數據&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;假如第一次握手可以攜帶數據的話，如果有人要惡意攻擊服務器，那他每次都在第一次握手中的 SYN 報文中放入大量的數據，然後瘋狂重複發 SYN 報文的話（因為攻擊者根本就不用管服務器的接收、發送能力是否正常，它就是要攻擊你），這會讓服務器花費很多時間、內存空間來接收這些報文。&lt;/p&gt;
&lt;p&gt;⭐ &lt;strong&gt;簡單的記憶就是，請求連接/接收即&lt;/strong&gt; &lt;code&gt;SYN = 1&lt;/code&gt; &lt;strong&gt;的時候不能攜帶數據&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;而對於第三次的話，此時客戶端已經處於 &lt;code&gt;ESTABLISHED&lt;/code&gt; 狀態。對於客戶端來說，他已經建立起連接了，並且也已經知道服務器的接收、發送能力是正常的了，所以當然能正常發送/攜帶數據了。&lt;/p&gt;
&lt;h3 id="半連接隊列"&gt;半連接隊列&lt;/h3&gt;
&lt;p&gt;服務器第一次收到客戶端的 SYN 之後，就會處於 &lt;code&gt;SYN_RCVD&lt;/code&gt; 狀態，此時雙方還沒有完全建立其連接，服務器會把這種狀態下的請求連接放在一個隊列裡，我們把這種隊列稱之為&lt;strong&gt;半連接隊列&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;當然還有一個&lt;strong&gt;全連接隊列&lt;/strong&gt;，完成三次握手後建立起的連接就會放在全連接隊列中。如果隊列滿了就有可能會出現丟包現象。&lt;/p&gt;
&lt;h3 id="syn-洪氾攻擊"&gt;SYN 洪氾攻擊&lt;/h3&gt;
&lt;p&gt;SYN 攻擊就是 &lt;strong&gt;Client 在短時間內偽造大量不存在的 IP 地址，並向 Server 不斷地發送 SYN 包&lt;/strong&gt;，Server 則回覆確認包，並等待 Client 確認，由於源地址不存在，因此 Server 需要不斷重發直至超時，這些偽造的 SYN 包將長時間佔用半連接隊列，導致正常的 SYN 請求因為隊列滿而被丟棄，從而引起網絡擁塞甚至系統癱瘓。&lt;/p&gt;
&lt;h3 id="如果第三次握手丟失了客戶端服務端會如何處理"&gt;如果第三次握手丟失了，客戶端服務端會如何處理？&lt;/h3&gt;
&lt;p&gt;服務器發送完 SYN-ACK 包，如果未收到客戶端響應的確認包，也即第三次握手丟失。那麼服務器就會進行首次重傳，若等待一段時間仍未收到客戶確認包，就進行第二次重傳。如果重傳次數超過系統規定的最大重傳次數，則係統將該連接信息從半連接隊列中刪除。&lt;/p&gt;
&lt;p&gt;注意，每次重傳等待的時間不一定相同，一般會是指數增長，例如間隔時間為 1s、2s、4s、8s&amp;hellip;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="tcp-四次揮手釋放連接"&gt;TCP 四次揮手釋放連接&lt;/h2&gt;
&lt;h3 id="四次揮手過程詳解"&gt;四次揮手過程詳解&lt;/h3&gt;
&lt;p&gt;建立一個 TCP 連接需要三次握手，而終止一個 TCP 連接要經過四次揮手（也有將四次揮手叫做四次握手的）。這是由於 TCP 的&lt;strong&gt;半關閉&lt;/strong&gt;（half-close）特性造成的，TCP 提供了連接的一端在結束它的發送後還能接收來自另一端數據的能力。&lt;/p&gt;
&lt;p&gt;TCP 連接的釋放需要發送四個包（執行四個步驟），因此稱為&lt;strong&gt;四次揮手&lt;/strong&gt;(&lt;code&gt;Four-way handshake&lt;/code&gt;)，&lt;strong&gt;客戶端或服務端均可主動發起揮手動作&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://live.staticflickr.com/65535/52676617649_2711bbb224_o.png"
max-width="100%"
max-height="100%"
class="gallery-image"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;回顧一下上圖中符號的意思：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;FIN&lt;/strong&gt;： 連接終止位&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;seq&lt;/strong&gt;： 發送的第一個字節的序號&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ACK&lt;/strong&gt;： 確認報文段&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ack&lt;/strong&gt;： 確認號。希望收到的下一個數據的第一個字節的序號&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;剛開始雙方都處於 &lt;code&gt;ESTABLISHED&lt;/code&gt; 狀態，假設是客戶端先發起關閉請求。四次揮手的過程如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;第一次揮手&lt;/strong&gt;： 客戶端發送一個 FIN 報文（請求連接終止：FIN = 1），報文中會指定一個序列號 seq = u。並&lt;strong&gt;停止再發送數據，主動關閉 TCP 連接&lt;/strong&gt;。此時客戶端處於 &lt;code&gt;FIN_WAIT1&lt;/code&gt; 狀態，等待服務端的確認。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;FIN-WAIT-1&lt;/strong&gt;： 等待遠程 TCP 的連接中斷請求，或先前的連接中斷請求的確認&lt;/p&gt;&lt;/blockquote&gt;
&lt;ol start="2"&gt;
&lt;li&gt;&lt;strong&gt;第二次揮手&lt;/strong&gt;： 服務端收到 FIN 之後，會發送 ACK 報文，且把客戶端的序號值 +1 作為 ACK 報文的序列號值，表明已經收到客戶端的報文了，此時服務端處於 &lt;code&gt;CLOSE_WAIT&lt;/code&gt; 狀態。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CLOSE-WAIT&lt;/strong&gt;： 等待從本地用戶發來的連接中斷請求&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;此時的 TCP 處於半關閉狀態，客戶端到服務端的連接釋放&lt;/strong&gt;。客戶端收到服務端的確認後，進入 &lt;code&gt;FIN_WAIT2&lt;/code&gt;（終止等待2）狀態，等待服務端發出的連接釋放報文段。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;FIN-WAIT-2&lt;/strong&gt;： 從遠程TCP等待連接中斷請求&lt;/p&gt;&lt;/blockquote&gt;
&lt;ol start="3"&gt;
&lt;li&gt;&lt;strong&gt;第三次揮手&lt;/strong&gt;： 如果服務端也想斷開連接了（沒有要向客戶端發出的數據），和客戶端的第一次揮手一樣，發送 FIN 報文，且指定一個序列號。此時服務端處於 &lt;code&gt;LAST_ACK&lt;/code&gt; 的狀態，等待客戶端的確認。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;LAST-ACK&lt;/strong&gt;： 等待原來發向遠程TCP的連接中斷請求的確認&lt;/p&gt;&lt;/blockquote&gt;
&lt;ol start="4"&gt;
&lt;li&gt;&lt;strong&gt;第四次揮手&lt;/strong&gt;： 客戶端收到 FIN 之後，一樣發送一個 ACK 報文作為應答（ack = w+1），且把服務端的序列值 +1 作為自己 ACK 報文的序號值（seq=u+1），此時客戶端處於 &lt;code&gt;TIME_WAIT&lt;/code&gt;（時間等待）狀態。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIME-WAIT&lt;/strong&gt;： 等待足夠的時間以確保遠程TCP接收到連接中斷請求的確認&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;🚨 注意！這個時候由服務端到客戶端的 TCP 連接並未釋放掉，&lt;strong&gt;需要經過時間等待計時器設置的時間 2MSL（一個報文的來回時間） 後才會進入&lt;/strong&gt; &lt;code&gt;CLOSED&lt;/code&gt; &lt;strong&gt;狀態&lt;/strong&gt;（這樣做的目的是確保服務端收到自己的 ACK 報文。如果服務端在規定時間內沒有收到客戶端發來的 ACK 報文的話，服務端會重新發送 FIN 報文給客戶端，客戶端再次收到 FIN 報文之後，就知道之前的 ACK 報文丟失了，然後再次發送 ACK 報文給服務端）。服務端收到 ACK 報文之後，就關閉連接了，處於 &lt;code&gt;CLOSED&lt;/code&gt; 狀態。&lt;/p&gt;
&lt;h3 id="為什麼要四次揮手"&gt;為什麼要四次揮手&lt;/h3&gt;
&lt;p&gt;由於 TCP 的&lt;strong&gt;半關閉&lt;/strong&gt;（half-close）特性，TCP 提供了連接的一端在結束它的發送後還能接收來自另一端數據的能力。&lt;/p&gt;
&lt;p&gt;任何一方都可以在數據傳送結束後發出連接釋放的通知，待對方確認後進入&lt;strong&gt;半關閉狀態&lt;/strong&gt;。當另一方也沒有數據再發送的時候，則發出連接釋放通知，對方確認後就&lt;strong&gt;完全關閉&lt;/strong&gt;了 TCP 連接。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;通俗的來說，兩次握手就可以釋放一端到另一端的 TCP 連接，完全釋放連接一共需要四次握手。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;舉個例子： A 和 B 打電話，通話即將結束後，A 說&amp;quot;&lt;strong&gt;我沒啥要說的了&lt;/strong&gt;&amp;quot;，B 回答&amp;quot;&lt;strong&gt;我知道了&lt;/strong&gt;&amp;quot;，於是 A 向 B 的連接釋放了。但是 B 可能還會有要說的話，於是 B 可能又巴拉巴拉說了一通，最後 B 說&amp;quot;&lt;strong&gt;我說完了&lt;/strong&gt;&amp;quot;，A 回答&amp;quot;&lt;strong&gt;知道了&lt;/strong&gt;&amp;quot;，於是 B 向 A 的連接釋放了，這樣整個通話就結束了。&lt;/p&gt;
&lt;hr&gt;</description></item><item><title>網站前端打 API 時把密碼加密，有意義嗎？</title><link>https://blog.wayne-project.com/2023-01-17/why-encrypt-in-client/</link><pubDate>Tue, 17 Jan 2023 00:00:00 +0000</pubDate><guid>https://blog.wayne-project.com/2023-01-17/why-encrypt-in-client/</guid><description>&lt;img src="https://live.staticflickr.com/65535/52634831533_259566b84f_o.jpg" alt="Featured image of post 網站前端打 API 時把密碼加密，有意義嗎？" /&gt;&lt;style&gt;
.article-content p code {
background-color: #f5f5f5;
color: #ff3860;
}
.focus {
background: #f1e2e2;
color: #d62c2c;
padding: 0 5px;
}
&lt;/style&gt;
&lt;p&gt;&lt;a class="link" href="https://www.facebook.com/groups/f2e.tw/posts/5689037364466915" target="_blank" rel="noopener"
&gt;參考網站&lt;/a&gt;&lt;br&gt;
&lt;a class="link" href="https://ithelp.ithome.com.tw/questions/10211642" target="_blank" rel="noopener"
&gt;參考網站&lt;/a&gt;&lt;br&gt;
&lt;a class="link" href="https://www.sjoerdlangkemper.nl/2020/02/12/the-case-for-client-side-hashing-logging-passwords-by-mistake/" target="_blank" rel="noopener"
&gt;參考網站&lt;/a&gt;&lt;br&gt;
&lt;a class="link" href="https://blog.huli.tw/2023/01/10/security-of-encrypt-or-hash-password-in-client-side/?fbclid=IwAR3KXQ6gSgO1uxzAhtplhEs-AtQ5KIg2GR0HRaKcbMewUrWgEi6JGas48T8" target="_blank" rel="noopener"
&gt;參考網站&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;最近有人在臉書前端交流社群發了&lt;a class="link" href="https://www.facebook.com/groups/f2e.tw/posts/5689037364466915" target="_blank" rel="noopener"
&gt;一則貼文&lt;/a&gt;，內容是他看到了一個問題：&lt;a class="link" href="https://ithelp.ithome.com.tw/questions/10211642" target="_blank" rel="noopener"
&gt;請問登入api傳賬號、密碼json明碼會有問題嗎?&lt;/a&gt;，想知道大家對這個問題的看法。&lt;/p&gt;
&lt;p&gt;而底下的回答大部份都是覺得「&lt;strong&gt;有用 HTTPS 就好了，沒必要額外再實作一層加密，沒有什麼太大的意義&lt;/strong&gt;」.&lt;/p&gt;
&lt;p&gt;老實說我以前也是這樣認為的，而且過去在社群中就有出現過類似的討論。我那時候想說都已經有 HTTPS 了，而 HTTPS 本身的目的就是為了保障傳輸的安全，為什麼要自己再做加密？&lt;/p&gt;
&lt;p&gt;但這一兩年接觸資安以後，我的想法有了改變，我認為前端在傳輸前把密碼加密，是有其意義的，而接下來我會詳細說明我的理由。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="定義問題"&gt;定義問題&lt;/h2&gt;
&lt;p&gt;在進入正題之前，我想把問題定義得更明確一點，免得出現一堆張飛打岳飛，拿兩個完全不同的情境來比較的狀況。在原文底下，就有不少留言在討論的是不同的問題，把問題先定義清楚滿重要的。&lt;/p&gt;
&lt;p&gt;首先，我們要比較的對象是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在使用 &lt;strong&gt;HTTPS&lt;/strong&gt; 的前提下，打登入 API 時不做任何事直接傳送密碼的明文&lt;/li&gt;
&lt;li&gt;在使用 &lt;strong&gt;HTTPS&lt;/strong&gt; 的前提下，打登入 API 時先把密碼加密，再傳送到 server&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;這邊需要注意的是「&lt;strong&gt;兩個狀況都是 HTTPS&lt;/strong&gt;」，所以如果你想講的是「&lt;strong&gt;沒必要自己發明新技術&lt;/strong&gt;」或是「&lt;strong&gt;自己發明新的加密方式不會比較安全&lt;/strong&gt;」等等，在這個前提下全都不適用。&lt;/p&gt;
&lt;p&gt;因為傳輸層還是靠 &lt;strong&gt;HTTPS&lt;/strong&gt; 在傳輸，所以在這個階段並沒有自己發明新的方式，我只是在應用層自己把傳輸的資料額外加密一層而已。&lt;/p&gt;
&lt;p&gt;再來，先不考慮成本這件事，純粹只從技術的角度去看可能有哪些優缺點(成本相關的最後再來討論)。&lt;/p&gt;
&lt;p&gt;最後呢，我這邊的情境是「&lt;strong&gt;加密密碼&lt;/strong&gt;」而非 hash，這是因為我覺得 hash 的狀況比較複雜，我自己想先用加密來舉例，而且這個加密是「&lt;strong&gt;非對稱式加密&lt;/strong&gt;」。&lt;/p&gt;
&lt;p&gt;也就是我們可以想像已經有一把 public key 存在於 client 端(當然，每個人都可以拿得到)，在送出 request 以前，會先使用 JavaScript 把密碼用 public key 加密以後再送出，而 server 使用 private key 解密，拿到密碼以後 hash 過再存入資料庫中。&lt;/p&gt;
&lt;p&gt;綜合以上，這篇想處理的問題是：「&lt;strong&gt;已經使用了 HTTPS，在打登入 API 以前先把密碼加密過或是不做任何處理，這兩者的差別在哪？&lt;/strong&gt;」&lt;/p&gt;
&lt;p&gt;而我們可以把回答分成兩個部分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;假設 HTTPS 被破解了，是不安全的，差在哪？&lt;/li&gt;
&lt;li&gt;假設 HTTPS 是安全的，差在哪？&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="假設-https-不安全差在哪"&gt;假設 HTTPS 不安全，差在哪？&lt;/h2&gt;
&lt;p&gt;首先，可以先想一下怎樣的狀況會造成 HTTPS 不安全，攻擊者掌握了系統的哪些部分？&lt;/p&gt;
&lt;p&gt;底下簡單分為四種狀況討論：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;攻擊者掌控整台電腦，信任惡意憑證&lt;/li&gt;
&lt;li&gt;攻擊者成功執行了中間人攻擊&lt;/li&gt;
&lt;li&gt;攻擊者可以在網路層監聽 request 並使用漏洞取得明文&lt;/li&gt;
&lt;li&gt;攻擊者直接針對 HTTPS server 進行攻擊&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="攻擊者掌控整台電腦信任惡意憑證"&gt;攻擊者掌控整台電腦，信任惡意憑證&lt;/h3&gt;
&lt;p&gt;若是這種類型的狀況，那當然是有沒有加密都沒差，因為攻擊者都有其他更好的手段去取得你的密碼。&lt;/p&gt;
&lt;h3 id="攻擊者成功執行了中間人攻擊"&gt;攻擊者成功執行了中間人攻擊&lt;/h3&gt;
&lt;p&gt;那如果是「攻擊者成功執行了中間人攻擊（Man-In-The-Middle）」呢？你的電腦沒事，只是封包在傳輸的過程中被中間人攻擊。&lt;/p&gt;
&lt;p&gt;在這樣的前提底下，沒加密的情形就能直接被獲取密碼，而有加密的情形攻擊者只能獲取到加密過的密文而非明文。但需要注意的是既然是叫中間人攻擊，那攻擊者除了監聽你的 request 以外，也能傳送偽造的 response 給你，把前端用來加密密碼的部分換掉。&lt;/p&gt;
&lt;p&gt;因此無論密碼是否加密，攻擊者都可以拿到明文，只是如果有加密的話，攻擊者取得密碼的成本較高（需要先找到在哪邊加密的，然後把那段改掉）。&lt;/p&gt;
&lt;h3 id="攻擊者可以在網路層監聽-request-並使用漏洞取得明文"&gt;攻擊者可以在網路層監聽 request 並使用漏洞取得明文&lt;/h3&gt;
&lt;p&gt;這個狀況跟上一個的不同之處在於這個只能讀，不能寫。若是有辦法解密 request 的封包，就能夠看到明文。&lt;/p&gt;
&lt;p&gt;所以如果有把密碼先加密，攻擊者就無法取得密碼的明文。&lt;/p&gt;
&lt;p&gt;這邊需要注意的是儘管無法取得明文，攻擊者依然可以透過重送請求來登入你的帳號（先假設沒有其他機制），所以你帳號還是被盜了，只是攻擊者不知道你的密碼明文。&lt;/p&gt;
&lt;p&gt;這有差嗎？有！&lt;/p&gt;
&lt;p&gt;假設他知道了你的密碼明文，就可以拿你的這組帳號密碼去各個服務都試試看，若是你其他網站也用同一組帳號密碼，就會跟著淪陷（俗稱的撞庫攻擊）。&lt;/p&gt;
&lt;p&gt;因此在這個狀況底下，加密密碼的安全性顯然是更高的。&lt;/p&gt;
&lt;p&gt;此時你可能會想問的問題是：「那在什麼狀況下攻擊者能夠取得 HTTPS 的明文？真的有可能嗎？」&lt;/p&gt;
&lt;p&gt;這邊有一份美國衛生及公共服務部 HHS 所做的簡報：&lt;a class="link" href="https://www.hhs.gov/sites/default/files/securing-ssl-tls-in-healthcare-tlpwhite.pdf" target="_blank" rel="noopener"
&gt;SSL/TLS Vulnerabilities&lt;/a&gt;，裡面有記錄一些 SSL/TLS 曾經有過的漏洞，所以取得 HTTPS 的明文確實是有可能的。&lt;/p&gt;
&lt;p&gt;不過光是知道「有可能」是不夠的，應該問的是「機率高嗎？」，在討論風險的時候，通常也會根據風險的高低以及嚴重程度來決定該怎麼處理這個風險。&lt;/p&gt;
&lt;p&gt;答案是「機率很低」，簡報裡的漏洞最近的已經是 2017 年的事情了，而且是使用一些舊的、有問題的加密演算法，除此之外還需要符合不少其他條件，才能執行攻擊，所以我認為機率確實是很低的。&lt;/p&gt;
&lt;p&gt;以 2016 年發表的 &lt;a class="link" href="https://drownattack.com/" target="_blank" rel="noopener"
&gt;DROWN(Decrypting RSA with Obsolete and Weakened eNcryption)&lt;/a&gt; 來說，Server 要支援 SSLv2，而攻擊者要能抓到加密過的 TLS connection，符合這些條件以後，進行一大堆運算後就可以解開 900 個 connection 中的 1 個，而運算成本在當時是 440 美金，約 13k 台幣。&lt;/p&gt;
&lt;p&gt;總之呢，針對這個狀況，我們可以說：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;假設攻擊者可以取得 HTTPS 的明文，那確實自己在應用層加密會更安全，但要符合這個假設的成本很高，發生機率很低&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id="攻擊者直接針對-https-server-進行攻擊"&gt;攻擊者直接針對 HTTPS server 進行攻擊&lt;/h3&gt;
&lt;p&gt;這個分類我指的是在 2014 年發生過的 &lt;a class="link" href="https://devco.re/blog/2014/04/11/openssl-heartbleed-how-to-hack-how-to-protect/" target="_blank" rel="noopener"
&gt;Heartbleed 漏洞&lt;/a&gt;，攻擊者可以藉由 OpenSSL 的漏洞讀取到 server 的記憶體。&lt;/p&gt;
&lt;p&gt;這個狀況跟上一個滿像的，如果有在 client 端先加密過密碼，那攻擊者在 server 上讀到的就是加密過後的，不知道密碼明文是什麼。&lt;/p&gt;
&lt;p&gt;所以結論跟上個一樣，就是加密密碼會更安全。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="中場總結"&gt;中場總結&lt;/h2&gt;
&lt;p&gt;剛剛我們討論了幾種「HTTPS 變得不安全」的狀況，從以往發生過的案例可以得知「HTTPS 變得不安全」是有可能的。若是攻擊者能夠讀到 HTTPS 傳輸的明文，那在應用層將密碼加密，就能防止攻擊者取得密碼的明文，因此會比沒有加密來得更安全。&lt;/p&gt;
&lt;p&gt;如果要講得更詳細，可以從兩個維度去切入，一個是嚴重性（severity），另一個是可能性（possibility）。&lt;/p&gt;
&lt;p&gt;以嚴重性來說，不管有沒有加密密碼，只要攻擊者有辦法拿到 request 的內容，你的帳號就已經淪陷了，而有沒有加密唯一的差別只有攻擊者是否能取得明文密碼，有的話就可以執行撞庫攻擊，拿密碼去試更多其他的網站。&lt;/p&gt;
&lt;p&gt;而可能性就是「HTTPS 的明文被拿到」這件事的可能性，從過往的經歷以及研究來看，雖然是有可能的，但在 2023 年的今天，機率是很低的。&lt;/p&gt;
&lt;p&gt;因此我們現階段的結論應該是：&lt;/p&gt;
&lt;p&gt;攻擊者若是可以繞過 HTTPS 拿到明文的 request，那在應用層將密碼加密，確實會比較安全，但要注意的是要達成這個前提非常困難，發生的機率極低。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="假設-https-是安全的"&gt;假設 HTTPS 是安全的&lt;/h2&gt;
&lt;p&gt;接下來我們討論第二種狀況，那就是假設 HTTPS 是安全的，沒有任何人可以從中間看到明文的內容，這應該也是留言區中大多數人假設的前提。&lt;/p&gt;
&lt;p&gt;在這種狀況下，會有哪些風險呢？&lt;/p&gt;
&lt;p&gt;有一個現實生活中會發生，而且也確實發生過的風險，那就是 &lt;strong&gt;logging&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;身為前端工程師，在前端加裝一些 error tracking 的服務是很合理的事情，若是我們直接實作一個「&lt;strong&gt;只要伺服器回傳 5xx，就把 request 記錄起來&lt;/strong&gt;」的機制，如果好巧不巧哪天登入的 API 出現這個狀況，你就可以在 log 裡面看到使用者的明文密碼。&lt;/p&gt;
&lt;p&gt;而且不只前端，後端也可能有類似的機制，碰到一些問題就把 request 整個寫到 log 檔去，方便以後查看以及 debug，一不小心密碼就可能被寫進去。&lt;/p&gt;
&lt;p&gt;在這種狀況下，在 client 端先把密碼加密顯然是有好處的，那就是在這些錯誤處理的 log 中，被記錄下來的密碼會是密文，除非你有密鑰，否則你是不會知道使用者的密碼的。&lt;/p&gt;
&lt;p&gt;我在網路上找到一篇跟我論點一樣的文章：&lt;a class="link" href="https://www.sjoerdlangkemper.nl/2020/02/12/the-case-for-client-side-hashing-logging-passwords-by-mistake/" target="_blank" rel="noopener"
&gt;The case for client-side hashing: logging passwords by mistake&lt;/a&gt;，裡面有附上很多參考連結，都是以前各大公司不小心把密碼的明文記錄下來的案例。&lt;/p&gt;
&lt;p&gt;然後有個小地方稍微講一下，上面這篇做的是「在 client 端 hash」，跟我這篇一開始設定的「在 client 端做非對稱式加密」有點不同，hash 會更安全一點，確保在 server 真的沒人知道你的密碼明文是什麼。&lt;/p&gt;
&lt;p&gt;總之呢，在 client 端先把密碼加密或是 hash，可以避免在日誌中不小心出現使用者的密碼明文，這個顯然是個額外的優點。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="加密還是-hash"&gt;加密還是 hash？&lt;/h2&gt;
&lt;p&gt;文章開頭我有提到 hash 的狀況有些複雜，所以我先把情境設定在「對密碼做非對稱式加密」再傳輸，因為對上面我舉的那些例子而言，這兩種情境的差異不大。&lt;/p&gt;
&lt;p&gt;舉例來說，HTTPS 被拿到明文內容，無論你對密碼做了非對稱式加密還是雜湊，在攻擊者無法取得伺服器端密鑰的前提之下，都是拿不到明文密碼的。&lt;/p&gt;
&lt;p&gt;那為什麼 hash 的狀況有些複雜呢？&lt;/p&gt;
&lt;p&gt;假設我們在前端先把密碼 hash 過後再傳到後端，那後端是要直接存進去資料庫嗎？如果直接存進去資料庫，哪天資料庫的內容曝光，攻擊者就拿到這些 hash 過的密碼了。&lt;/p&gt;
&lt;p&gt;通常在有加鹽以及雜湊演算法夠強的前提之下，被拿到 hash 的密碼還是能保證一定的安全性，可是在這種情況下，反而變得很不安全。&lt;/p&gt;
&lt;p&gt;因為前端傳給後端的內容已經是 hash 過的了，所以攻擊者可以直接拿 hash 過的密碼進行登入，根本不用知道明文是什麼。雖然保護了明文，但失去了原本雜湊的安全性。&lt;/p&gt;
&lt;p&gt;因此如果要做 client side hashing，server side 收到後也要再做一次。如此一來，就算資料庫被偷走，攻擊者也沒辦法利用資料庫中的 hash 直接登入。&lt;/p&gt;
&lt;p&gt;有些人可能跟我一樣好奇：「做兩次 hash 不會更不安全嗎？」，我們可以看一下 Google 在 &lt;a class="link" href="https://cloud.google.com/static/solutions/modern-password-security-for-system-designers.pdf" target="_blank" rel="noopener"
&gt;Modern password security for system designers&lt;/a&gt; 裡面怎麼說：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Have the client computer hash the password using a cryptographically secure algorithm and a unique salt provided by the server. When the password is received by the server, hash it again with a different salt that is unknown to the client. Be sure to store both salts securely. If you are using a modern and secure hashing algorithm, repeated hashing does not reduce entropy.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;看起來是還好，問題不大。&lt;/p&gt;
&lt;p&gt;總之呢，看起來最安全的但也更複雜的解法就是 client side 先 hash 一次，然後丟到 server 的時候再 hash 一次存進資料庫，如此一來就可以保證：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;HTTPS 因為各種原因失效時，攻擊者無法取得明文密碼&lt;/li&gt;
&lt;li&gt;在 Server 端，沒有任何人知道使用者的明文密碼&lt;/li&gt;
&lt;li&gt;明文密碼不會因為人為失誤被記錄到 log 中&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;那如果真的比較好用，為什麼沒人在用？&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="現實生活中到底有誰在前端做-hash-或是加密"&gt;現實生活中，到底有誰在前端做 hash 或是加密？&lt;/h2&gt;
&lt;p&gt;當我一開始碰到這個問題，講出「怎麼沒人在用」的時候，其實只是「我自己沒碰過有人這樣用」，但實際上我並不知道那些知名網站的登入是怎麼做的。&lt;/p&gt;
&lt;p&gt;因此呢，我就直接去看了幾個知名網站的登入機制，我們一起來看一下結果，為了方便觀看，我把跟帳號密碼無關的內容都拿掉了。&lt;/p&gt;
&lt;p&gt;我在測試的時候，基本上都是用 test 或是 &lt;a class="link" href="test@test.com" &gt;test@test.com&lt;/a&gt; 搭配簡單的密碼如 1234 在測試，然後觀察 request 的內容。&lt;/p&gt;
&lt;p&gt;先來看一下 FAANG 吧！&lt;/p&gt;
&lt;h3 id="facebook"&gt;Facebook&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://zh-tw.facebook.com/login" target="_blank" rel="noopener"
&gt;https://zh-tw.facebook.com/login&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test@test.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;encpass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#PWD_BROWSER:5:1673256089:AbJQAJUvZZNvh2dZbeDqdu9dp7HWwyHOl3+0sCGjiHMMjvYdxJokpdHE/O+E5LIbnakRmDWQfV40ZaB31MaNXFYo1b+RI+LHh6MAdDPa4PJ+BesDp4u8B4F4diVQ+q7idbEhT5wTNaU=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;沒想到 Facebook 就是有實作前端加密的網站！後面那段 Base64 並不是直接把密碼 Base64，而是把加密過的密碼做 Base64，解出來是這樣：&lt;code&gt;\x01²P\x00\x95/e\x93o\x87gYmàêvï]§±ÖÃ!Î\x97\x7F´°!£\x88s\f\x8Eö\x1DÄ\x9A$¥ÑÄüï\x84ä²\x1B\x9D©\x11\x985\x90}^4e wÔÆ\x8D\\V(Õ¿\x91#âÇ\x87£\x00t3Úàò~\x05ë\x03§\x8B¼\x07\x81xv%Pú®âu±!O\x9C\x135¥&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="amazon"&gt;Amazon&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://www.amazon.com/ap/signin" target="_blank" rel="noopener"
&gt;https://www.amazon.com/ap/signin&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test@test.com&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1234&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="apple"&gt;Apple&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://idmsa.apple.com/appleauth/auth/signin" target="_blank" rel="noopener"
&gt;https://idmsa.apple.com/appleauth/auth/signin&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;accountName&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;test@test.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1234&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="netflix"&gt;Netflix&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://www.netflix.com/tw/login" target="_blank" rel="noopener"
&gt;https://www.netflix.com/tw/login&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;userLoginId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test@test.com&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1234&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="google"&gt;Google&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://accounts.google.com/v3/signin/_/AccountsSignInUi/data/batchexecute" target="_blank" rel="noopener"
&gt;https://accounts.google.com/v3/signin/_/AccountsSignInUi/data/batchexecute&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;f.req=[[[&amp;#34;14hajb&amp;#34;,&amp;#34;[1,1,null,[1,null,null,null,[\&amp;#34;1234\&amp;#34;,null,true]]]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;看來 FAANG 裡面，只有 Facebook 是有實作的。&lt;/p&gt;
&lt;p&gt;接著我突然好奇起其他常用服務的登入有沒有做，底下貼結果。&lt;/p&gt;
&lt;h3 id="github"&gt;GitHub&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://github.com/session" target="_blank" rel="noopener"
&gt;https://github.com/session&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;login&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test@test.com&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1234&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="microsoft"&gt;Microsoft&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://login.live.com/ppsecure/post.srf" target="_blank" rel="noopener"
&gt;https://login.live.com/ppsecure/post.srf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;login&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test@test.com&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;passwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1234&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="ibm-cloud"&gt;IBM cloud&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://cloud.ibm.com/login/doLogin" target="_blank" rel="noopener"
&gt;https://cloud.ibm.com/login/doLogin&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;test@test.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1234&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;看來有實作的是少數，那資安廠商呢？資安廠商自己有做嗎？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;資安廠商&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="kaspersky"&gt;Kaspersky&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://eu.uis.kaspersky.com/v3/logon/proceed" target="_blank" rel="noopener"
&gt;https://eu.uis.kaspersky.com/v3/logon/proceed&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;login&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;test@test.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;12345678&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="趨勢"&gt;趨勢&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://sso1.trendmicro.com/api/usersigninauth" target="_blank" rel="noopener"
&gt;https://sso1.trendmicro.com/api/usersigninauth&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;email&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;test@test.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;12345678&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="tenable"&gt;Tenable&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://cloud.tenable.com/session" target="_blank" rel="noopener"
&gt;https://cloud.tenable.com/session&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1234&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="proton"&gt;Proton&lt;/h3&gt;
&lt;p&gt;這個應該不算資安廠商，但突然很好奇強調隱私的 Proton 是怎麼做的，一看發現好像很複雜。&lt;br&gt;
在登入的時候會先把 username 送過去，拿到一些看起來是 key 的東西。&lt;/p&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://account.proton.me/api/auth/info" target="_blank" rel="noopener"
&gt;https://account.proton.me/api/auth/info&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;Username&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;test@test.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;Code&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;Modulus&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA256\n\nu9K5yr97L9VV2ijOSI62tJcewUiRhQa8qJa24baNpGyw0lf3JLiF4fxUHqTErwF9UdoxE0z4Kb147naphylBFddyKsjhzHNcxk2rBw9haiPxD69BrVYm0n+LVlPqmjXFF7btr1H7oqHGX4b4Dy9omL/KaZz/Dco2NEhw0UBhEZbTAs6Ch01ur9XLbSOI7yb6MRsqCehfy82gDTdbPtXvqQsQjg5XoC2Ib2qTYFaU/24mq/gOaMbVuAGX0hBYzr5NpN9ol2XCdHOLg28Xe90+kisg39VV04axy7Ndvh489dC1CxjcWSSpXd6cPJyOn/HH9aPeTZeucBllRGbPgwR6/w==\n-----BEGIN PGP SIGNATURE-----\nVersion: ProtonMail\nComment: https://protonmail.com\n\nwl4EARYIABAFAlwB1j0JEDUFhcTpUY8mAAD1GwEAoC91QCSfXPEuWM13NZvy\nvL9NQIABuSrVOvgJwMhUTnUBAPb4zbIdTYFOQNrPLvonJt2mmRNy4lGcW7uN\n5yHzJ18J\n=Oykn\n-----END PGP SIGNATURE-----\n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;ServerEphemeral&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;DY6eRYM1bqYZZ5jzZFdWv88tKYP2PnS0y4A+f7/eqMXj8wB2VefV2kfIDrZ5AorWfDzBq4wMtNG2k5dzbT2qWppzpvltrSl2Nm4i8eWIRVxXWHl/46dGuPXFHUcXBNMP3XEQvft0YEbHOPO9Es0RZRaObV5XPFyx6kzOJxXc1tIt4PfbhODMfsAoy/yxt6eLN3HUiORCBOvzsH2sfG99Gx1YSAe3GL6g/K+bdg59eglueXRESoB0/VFRsvQevi9nVXx/JZNTG0U4BBUOlMjpYYMgEP6eQgZZ/09ZPYD3a2tW65mSnNt6lSDfwiKj02UuDqymTvj7mYm44T0SuAocwg==&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;Version&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;Salt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;dI7OcD+K4rGPBA==&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;SRPSession&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;3fa6224285409b6af07c811971e05341&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;接著輸入密碼登入時，會送出如下的 request，看起來也很複雜：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;ClientProof&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;I9Nfd0Nd3OzODf2nt9zLxFHWogEwfRje8zjoeZnblyLfyzz23uXTjJ4qgRFomjIEEtZrlM1jTQa4wRIMGIIV7E6pMqq8c6wcc2tegP4Xt76S0EbnVtE1F9i0Wj46aCPUM0Mha3Zmgi9LKerrGlaftr2FBedjPFT9rPrbLqRQcFNMD33tn69gD/p28q4RAr3/7d/tz7TYhytD5oxCAUwrkqiZOi0kg//2mUJ9YNT2nWcgqUERoaU51NbNMcaPnMteEe1PlIJdiQbvNa5K07u8rk7itpBrGW2FP26bREp0UMTzNYM5HcDDkmp4dp9GoBjFJL9n0THUdt/oRRJ/Enj5WQ==&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;ClientEphemeral&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;D013N7FXYHylqMeWa6ctJIv3J4uF1hqodyYfw6O+Sj7MZOIB+wksfgk/nkXCmRxQhuSYwqwMJIpyFD3MEolOZAHMU2n6HQlxe9A4KbrE4gk3UiGwfgcZDmFejTmMMxfWhf4zO2Z1fBbohreqwwN0mz3AqqsfE5dsDh3LEfkiJB449YGZfHeUHyIzS1jTmnx/8l6uVSKwJDCJelVFYKMXrxVt0ltcGRoYD92MUj82kR0am+BN4+djHyYYXuwuIYArnTW4kDP3T2yCIAMVgZnFaUCc2gfynt40mQP4q87jmMELOl8TDIDo5iKyH4gJc/470qIuIyj4ffVLiZ7t8S+kcw==&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;SRPSession&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;3fa6224285409b6af07c811971e05341&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;Username&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;test@test.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;Payload&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;qcA_CRYU6gSyHWdn&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;c6UZSKPo4Sfm/3+DvQN72TTxyj+/TplKT9edDiUI5wMfGUsoJs9FGerOtkoW8T49r7KOvqHkzS2+M2v8ra7J9l5kSf5jgC9ZvgZ8Ja5Xgg02nxgAABydOirGLoL4htFsYVtwLrNg8NeSEanLwYLCVaSqkjANRJks0eaKpUOd8xRhCFtUH/GCbyg27oZfzDsqKXemKprOUsOh42NTqzEmruAkxs2x8mUsLy/vXptVAdaiJLrsSRqD0YBGjvOp4W2/0g6V2zfedJpJEzVwtSi1vXTC5bwxmEJlYdV9AiQECogAAJFxLQi7JjtmgFe4tNcv97JD0B8giZ6XS35swjz0vz0mOjVBUwmiDa8n54Y5kBaAoZe5pijdp2S4SOcRAknDIcD1nf0v7oSMOE9WtH/sa+XI1D2s5lFKo/iInf7r5R9src2hHFoy0b2XT0oCfLPwFX87yjaKbf7bbkjByx/3dOgzEliAkS6nHK+fmeDDVM4EoZqVSKZHLg3QTcg4DKaICyDsotALr2UqI/ARzkX4yhAXz5xHFaxl6hWAKLJPJcgk6il6oX0s0PCBNSY0Fi3vbQvXD4WalUx+LBNto6CUqeAIzVuAh8sCubzufoSORypE5WqfnuJzAlZ9sMEjaQycuRi497aV3jmjgx53UwO0OiZGxDTEMFBcov4P0g1blZ4vxmULhZU0RfdP31udLr6GTCAB90CM6Vk9w9CsYM+hmo3+JpEAtIVgLVVqcPikTbV+yaOJ1RknxBf3g06kTl0LQ+zBV6pG2rFVi8G4XT9L4FsIgxTNsl/ryzs8vJU7K+HvyE1Lp2pAXrfcju7TAIqK/FOXvp1c8Ay9O6d4fmd/PZalnRDv5mQ6Gmd6JSNzNh6i6AibBuF13w3OBaulY3FGNU/cH/AXLBIqjSzf/OySwkKkC9HBurSs3D0zqcH9BwUpmPEL8jbc8yPE+hPAim+tDo1BXCQNClxgGLaI6FXkuCiQ4AHiKsq0xs5b3WAFzcvBv1rc003RWxRegH/2teIooKU9w1kDPQRaK8/rIYe8u+BlBeZq4OwCXxx56JHfmTxtJwBi95KqsWzLGtY3ILcb+/XkzSRmE2TWbkW1IXzRsl8F6NSJj7JnHA3UrQf4hxuwbaYxpKJrcHuHc8e1wxqXrUSKooCOUxwSBgxvLLT37eaByNTxpfWomxIsH671wuydnmMedWyNIqyaMtxBORuiWUiG4jbMC2BjrVptXJ7VWigf3Vy5OQlMOyTx8tLWi1qZODYyywMBAvHYQlFfSqmIrm4y4dmK/srJE/+daEnNS+kWF48Jm/rQORO5AUwqWL+Lefg9pchcL1BnHOANcviO8pAkxLo8TiK7VLKI5/xUsZQoQSlhRt27zMF+sIv+exY375HApiY+a1VQ6OqE4Nvba7O8ETLoLFg4a8Aj+W8erXFHW5F0vVIRphAve9orM4QYnAmOigFAiLb0Pxx124wUjFR9s5oP98hAtNL/t+uGAXrb0oxiCfyHb9wa2Qb0x6o9FpuBIc5ZXId+cEXEvOdqhnUQ7ZuOi/fX81hlqgUaiD/A6P+zjAcREXdktd+hrhSXwCIKSBkp/mNymnalQKJkLaNVT+W2sOWqXxTSTIytCQx36xABcj1BXRApntob6Qvche8QJLTjzr9bDpn+Mo59N9PSU51DPIj5Avre6ChTHEQvjz9s1IM2XroBX/KFBnPj33aYQZyov4uxrVXxic+fiY+fLMF8x1ut/eNWeQU6fn+rU5PEGQ9bbAsjVBZYA5H93ROhO5lnSxoEk5PHkgQ9WpxueckPjJIUGAs+O8QMRFicccfKjhNIc32rXTqbVqLyoz62riDn8Y18MUBoeI8ORyqZOKEEBFsi5dwqoq8t82NFdx5LFjsLdk4RmMXZ2uygNLk8gH2Yyfu3iOQS2bKtNCW42Xmo66Xu5kt8NwAneYQK0mTn6HUv94K10J4hY+Q=&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;補充：經討論串有人提醒後發現這是一個叫做 SRP（Secure Remote Password）的協議，Proton 有提供一個 &lt;a class="link" href="https://proton.me/static/9f5e7256429a2f674c943c5825257b82/protonmail_authentication_excerpt.pdf" target="_blank" rel="noopener"
&gt;ProtonMail Security Features and Infrastructure&lt;/a&gt;，裡面有記錄他們的安全措施，就有講到這個機制。&lt;/p&gt;
&lt;p&gt;看起來滿複雜要花不少時間研究，先放著，有興趣的可以參考：&lt;a class="link" href="https://blog.amis.com/srp-1f28676aa525" target="_blank" rel="noopener"
&gt;SRP — 更健全的登入及資料傳輸保護協議&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;雖然更安全，但成本應該又更高了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="交易所與銀行"&gt;交易所與銀行&lt;/h2&gt;
&lt;p&gt;看完上面案例發現有做的是少數，因此我好奇更注重安全的加密貨幣交易所與傳統銀行有沒有做。&lt;/p&gt;
&lt;h3 id="binance"&gt;Binance&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://accounts.binance.com/bapi/accounts/v2/public/authcenter/login" target="_blank" rel="noopener"
&gt;https://accounts.binance.com/bapi/accounts/v2/public/authcenter/login&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;email&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;test@test.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;fe2e6b4138fcd7f27a32bc9af557d69a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;safePassword&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;d404559f602eab6fd602ac7680dacbfaadd13630335e951f097af3900e9de176b6db28512f2e000b9d04fba5133e8b1c6e8df59db3a8ab9d60be4b97cc9e81db&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="coinbase"&gt;Coinbase&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://login.coinbase.com/api/v1/authenticate-credentials" target="_blank" rel="noopener"
&gt;https://login.coinbase.com/api/v1/authenticate-credentials&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;email&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;test@test.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1234&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="kraken"&gt;kraken&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://www.kraken.com/api/internal/account/settings/tfa" target="_blank" rel="noopener"
&gt;https://www.kraken.com/api/internal/account/settings/tfa&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1234&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="玉山銀行"&gt;玉山銀行&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://ebank.esunbank.com.tw/fco/fco08001/FCO08001_Home.faces" target="_blank" rel="noopener"
&gt;https://ebank.esunbank.com.tw/fco/fco08001/FCO08001_Home.faces&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;loginform:custid&lt;span class="o"&gt;=&lt;/span&gt;A0000...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;loginform:name&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;mxagZmaqygDx0XX6784Svw&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;__NgZQcFfAx+lQmPza2eNpOA&lt;span class="o"&gt;==&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;loginform:pxsswd&lt;span class="o"&gt;=&lt;/span&gt;8,lIRnuUxw/yStOt9QIYG2U3Gn2XkG03x4Ey/UU6JGtsbUxfRXoAv9CjE3EWerDN3tfx3dD/B3ChLAPMSG2BA3jMXUCZC06y8UbQ5isKc9fCWZSSZAWWcOmJ7LdXw1ZhjV55hpw1upvAr9WEmZ0XF6x7if+dBxJ4KZ00d83qA9eA+3VaSk+JLhN8/CFBfTKTfJEs3PDNsm12XzRUBb4YE1aPQosVX10mdvh3zY5lmkrKuq8gnuImEf3oLOk4EF3eVpr6jJiFzMKlHybvGdtKYS25+pgTS68wn3v023barbSmgivcv5atm0XsyXWDY2dKEtdQz+7A6R+AB0bExbQlRjqQ&lt;span class="o"&gt;==&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="國泰世華"&gt;國泰世華&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://www.cathaybk.com.tw/MyBank/Quicklinks/Home/NormalSignin" target="_blank" rel="noopener"
&gt;https://www.cathaybk.com.tw/MyBank/Quicklinks/Home/NormalSignin&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;CustID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;A000...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;UserId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;DC0C6E52BE2A2354C53401207F220F1B
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;Password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8cf5e1977f149ed0362629007a7f91d0efc7b12cb1895ba701c528a12b38d12f8148ca03ee671fe25d2a3a807be980f7728566e359a675734ce046899b147658388bb60f9b900e2ccc9adac280b54b5f2e28cb7eee1b634d0e1ed1c0c0c598c350f61eb003405559331a7f047add7289466bf42cfd5b9e774a1fa116af4fd7050adb8f174d42a8e2098a014a788bd2ffae3bf4ff7a8d8d7e2e8068402fda395da41be6e5d32f2d32cbee2afc26e82c58b60357b5cb186a3b9cf69df2deb9da8c9fde45337935180cb4e177109413d7a758d38bfc8334a4509d8d8fb6a37080f0e0086b4a5ef68f7809ca2ef97183b7f66d996873bb7dbfcee61d2da424b8b968
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="中國信託"&gt;中國信託&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://www.ctbcbank.com/IB/api/adapters/IB_Adapter/resource/preLogin" target="_blank" rel="noopener"
&gt;https://www.ctbcbank.com/IB/api/adapters/IB_Adapter/resource/preLogin&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;rqData&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;custId&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;A00....&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;878dbee38bbb4d77a30ee128f55f7bfe2169e45380d62a75453d3ca175e8ce8b|43d0499147b62adeec4eef3c77d33171b4569d0bdf7bbbe2b8b9bde3d30a26aba69aadfb28dfbaa9a997a0ccf668aaab0b6da582275175272172569a58a60bbfc5ac3a8c6862ce31f86247d7c1adf307e363c0f251fb88c4d39afa6ed0ca0a49e053f4f90000fa77b4e78beaead72ebdf52a13ecb4f20ae9a532947fad8156d5ec69d6763243364e71659079e469d1e01d0c384b0c71f4e9e524890227d82a51a340ef0b48638e05e347d75cb93d4a825a2bce6a90ef47f512351ee2d0d1ea17fb8afd521e427578603ea775191711f81d8dcb18e46b72daf3a49a60e50d12d3887e3bafab3758730f7fb0276373ebe1da01a03162ec8e73a202091a51b7f88d&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;userId&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;bfcdb9b2d6896a3bfb4a6542e8fb2689486d000b11bdc0c7bc336a6534aec74c|1b1a758bb26702bc0ac7cd660da2a72866f2cfdcf3668f2d39a5f8b006854f52a08f418b0a460b36374f95b7a310d73ea9994788698041f524ecd1f153448ab5d51f901a9a08ac2a9ee04c5c273ecb9d4ec1b6a62e9696c6126271e2f8c334fe17ce8b8538139363b90be75c1130cb251ec240bd26c920b52f5be9fc59094ce7d935d826242d69dc1ff7047a5abbf11d3c7de639a14bb10230912903cd948c05b3b3cb0cdb100f979640e291774e623a7109bde7b55bb8a6a373c0ca12820b072132ea61c845e60e26d09c7ee0fe23f7de286cbccb067a86fd1985c5b455f9ae46ce24dc8f52bcb05c205d6a462345162ae82c35e045bf3fd43a297c3edcfe17&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="美國銀行"&gt;美國銀行&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://m.globalcard.bankofamerica.com/pkmslogin.form" target="_blank" rel="noopener"
&gt;https://m.globalcard.bankofamerica.com/pkmslogin.form&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fcc63767-1a43-4cc6-8c3e-1346350b5274
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;12345678&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="新加坡星展銀行"&gt;新加坡星展銀行&lt;/h3&gt;
&lt;p&gt;API 網址：&lt;a class="link" href="https://internet-banking.dbs.com.sg/IB/Welcome" target="_blank" rel="noopener"
&gt;https://internet-banking.dbs.com.sg/IB/Welcome&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;請求內容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;USER_LOGON_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test123
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ENCRYPTED_PIN_BLOCK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;A8C48B7572A1A53C5A66E9B43365027C7FBF14BF461F480A46781E49648A8F70271A29C374F86FCD55A76ED17B2284B47C799B74475F29749D68631FF7E322177A21EEE8C41D8950638A2828C34A2653D7C9F69F5DA568E42D64CE89FCE8F024217B235835E6F8BC3C536F56361EDF459AFCE9A512BDBACAB2D25423209996C2E84A18EA8446685DAF9FAD4B1D6D8DF0F378EC27D9A81AD4D1A2B91BA3CFD838140A9BD48AD8D38D33B0093110BD1CA2C76F3DE4CBD969A9B0260DB890E9B1A99DC1193BFE9A1EDB3E56F71CB1CD8630558B242B040F733A4A40B2E17DE6DA03A58DEC8BB12DA87BB25971E2DBE5AF7AE6112266A3F9027B449BDF46D8DC0A1A
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="結論"&gt;結論&lt;/h2&gt;
&lt;p&gt;在隨機想到的 20 個網站中，有 7 個有在前端做加密或是 hash（我懶得看是哪個了，總之有做事就對了），名單是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Facebook&lt;/li&gt;
&lt;li&gt;Proton&lt;/li&gt;
&lt;li&gt;Binance&lt;/li&gt;
&lt;li&gt;玉山銀行&lt;/li&gt;
&lt;li&gt;國泰世華&lt;/li&gt;
&lt;li&gt;中國信託&lt;/li&gt;
&lt;li&gt;新加坡星展銀行&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;雖然說 35% 看起來很高，但那是因為銀行佔了大多數，一般網站則是很少實作這個機制。&lt;/p&gt;
&lt;p&gt;寫到這邊，可以來下結論了。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一個結論是：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「在 client 端傳送密碼前先把密碼加密或是 hash，確實能夠增加安全性」&lt;/p&gt;
&lt;p&gt;理由是做了以後，能夠達成以下事項：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;HTTPS 因為各種原因失效時，攻擊者無法取得明文密碼&lt;/li&gt;
&lt;li&gt;在 Server 端，沒有任何人知道使用者的明文密碼&lt;/li&gt;
&lt;li&gt;明文密碼不會因為人為失誤被記錄到 log 中&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以上都是沒有在 client 加密或是 hash 時做不到的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第二個結論是：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「確實有些大公司有做這個機制，但是非大多數，不過在銀行業似乎是主流」&lt;/p&gt;
&lt;p&gt;這個結論上面有貼完整的資料了，一般的網站很少做這個機制，但還是有人做。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第三個結論是：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「雖然從技術上來看能夠增加安全性，但實際上是否實作，仍然要考慮其他因素」&lt;/p&gt;
&lt;p&gt;這因素就是我前面提過的「可能性」還有開頭我講先不談的「成本」。&lt;/p&gt;
&lt;p&gt;若是真的比較安全，為什麼一般網站不會實作這個機制？&lt;/p&gt;
&lt;p&gt;或許是因為覺得 HTTPS 被攻破的可能性太低，低到可以忽略不計（我相信這是大多數留言的人覺得不需要做的理由，我也同意這點），也或許是成本太高，會增加程式碼複雜度；若是採用加密的方案，也會消耗更多運算的資源在加解密上面，這也是成本。&lt;/p&gt;
&lt;p&gt;這就是我覺得應該講清楚的地方。&lt;/p&gt;
&lt;p&gt;在前端先做 hash 或是加密，它確實是有優點的，不是多此一舉，也不是沒有意義，更不是讓系統變得更危險。&lt;/p&gt;
&lt;p&gt;但這不代表每個系統都該實作這個機制，因為它帶來的效益或許沒有成本高，這個端看各個公司的考量。對大部分的公司來說，與其為了 HTTPS 失效這個極低的可能性去投入成本，不如把時間花在加強其他登入環節的安全性（例如說 2FA 啦，或是不同裝置登錄警告等等），帶來的效益會更高。&lt;/p&gt;
&lt;p&gt;有些服務還會選擇把整包 request 都加密，而非只有密碼，這個又更安全但是成本又更高，而且 debug 很不方便。雖然說既然加密做在 client 端，攻擊者一定有辦法逆向這個機制，看出是怎麼做的，但這不代表這些機制沒幫助。&lt;/p&gt;
&lt;p&gt;舉例來說，假設我有個搶票 App 不想讓別人知道 API 怎麼呼叫，於是就實作了一個超複雜的加解密機制，儘管高手還是可以做逆向工程，寫出一個搶票機器人，但這個機制增加了他的時間成本以及對技術的要求。&lt;/p&gt;
&lt;p&gt;以技術上來說，就算理論上一定會被破解，這些機制還是有意義的，它的意義在於增加破解難度，加殼、混淆都是一樣的，不會因為「在 client 端的東西一定會被看穿」而不去做這些機制。&lt;/p&gt;
&lt;p&gt;重點在於你想保護的商業邏輯的價值，有沒有高到你需要付出這些成本去做額外的安全機制。&lt;/p&gt;
&lt;p&gt;最後，如果你需要一個條列式的簡單結論，會是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;無論如何，一定要先用 HTTPS&lt;/li&gt;
&lt;li&gt;在前端先把密碼做加密或是 hash 後再傳送，能夠增加安全性，但同時也帶來不少成本&lt;/li&gt;
&lt;li&gt;如果你是銀行或需要同等安全性，再來考慮要不要做這個，否則極大多數的狀況下，你不需要這個機制就夠安全了，資源投入在其他地方的效益會更大&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;若是對這個結論有不同意見，或是有在文章中發現哪些邏輯錯誤或技術錯誤，歡迎留言指正與討論，感恩。&lt;/p&gt;
&lt;p&gt;補充一下，這篇大多數從技術面來看，除此之外還可以從法遵面或是資安的實務經驗來看，但這些面向我就零經驗了。許願一下有相關經驗的人出來指點迷津，或許會有不同觀點。&lt;/p&gt;
&lt;hr&gt;</description></item><item><title>面試常見的瀏覽器問題(三) - HTTP/1、HTTP/1.1 和 HTTP/2 的區別</title><link>https://blog.wayne-project.com/2022-12-19/browser-http1.0-http1.1-http2.0-difference/</link><pubDate>Mon, 19 Dec 2022 00:10:00 +0000</pubDate><guid>https://blog.wayne-project.com/2022-12-19/browser-http1.0-http1.1-http2.0-difference/</guid><description>&lt;img src="https://live.staticflickr.com/65535/52571909202_4ddc6d0335_o.jpg" alt="Featured image of post 面試常見的瀏覽器問題(三) - HTTP/1、HTTP/1.1 和 HTTP/2 的區別" /&gt;&lt;style&gt;
.article-content p code {
background-color: #f5f5f5;
color: #ff3860;
}
.focus {
background: #f1e2e2;
color: #d62c2c;
padding: 0 5px;
}
&lt;/style&gt;
&lt;p&gt;&lt;a class="link" href="https://zh.wikipedia.org/zh-tw/HTTP/2" target="_blank" rel="noopener"
&gt;參考網站&lt;/a&gt;&lt;br&gt;
&lt;a class="link" href="https://www.explainthis.io/zh-hant/interview-guides/browser/http1.0-http1.1-http2.0-difference" target="_blank" rel="noopener"
&gt;參考網站&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;從 1989 年起，HTTP 經歷了數代的演化，從 1 到 1.1 再到 2 以及目前的 3，這些不同版本有些什麼不同呢？不管是前端或後端的面試，都是經常會考的。這篇我們會先針對 1 到 1.1 再到 2 之間的異同處做摘要。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="出現時間"&gt;出現時間&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;超文本傳輸協定 HTTP&lt;/strong&gt; (&lt;strong&gt;H&lt;/strong&gt;yper&lt;strong&gt;T&lt;/strong&gt;ext &lt;strong&gt;T&lt;/strong&gt;ransfer &lt;strong&gt;P&lt;/strong&gt;rotocol) 是在網際網路中用來通信數據的基礎。HTTP 一開始的發展是從 1989 年所發起制定，其中經過幾個時期的演進，HTTP/1.1 是隨著在 1999 年 6 月公布的 &lt;a class="link" href="https://tools.ietf.org/html/rfc2616" target="_blank" rel="noopener"
&gt;RFC 2616&lt;/a&gt; 所發表 。HTTP/2 (最初命名為 HTTP 2.0) 則是在 2015 年 5 月以 &lt;a class="link" href="https://www.rfc-editor.org/rfc/rfc7540" target="_blank" rel="noopener"
&gt;RFC 7540&lt;/a&gt; 正式發表，並取代 HTTP/1.1 成為 HTTP 的實作標準。截至 2021 年 10 月，全球有 46.5%的網站支援了 HTTP/2 (&lt;a class="link" href="https://zh.wikipedia.org/zh-tw/HTTP/2" target="_blank" rel="noopener"
&gt;wiki&lt;/a&gt;)。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="http1-和-http11-差異"&gt;HTTP/1 和 HTTP/1.1 差異&lt;/h2&gt;
&lt;p&gt;往下讀之前，要先理解之所以會有 HTTP/1.1 是因為 HTTP/1 有一些不那麼理想的地方。因此建議不要死背差異，而是從「 HTTP/1.1 解決了什麼問題」出發來理解。&lt;/p&gt;
&lt;h3 id="持久連接-keep-alive"&gt;持久連接 (keep-alive)&lt;/h3&gt;
&lt;p&gt;HTTP/1 在發送每個請求之前都需要建立一個新的連接，而每次連接都是有成本的，這種每次重連的方式會造成很多頻寬的浪費，以及時間的延遲。而 HTTP/1.1 默認使用持久連接，讓 HTTP/1.1 可以使用同一個 TCP 連接來重複多個 HTTP 請求，這麼一來就可以避免每次重新建立連接造成的頻寬浪費、時間延遲。&lt;/p&gt;
&lt;h3 id="狀態碼-100-continue"&gt;狀態碼 100 (Continue)&lt;/h3&gt;
&lt;p&gt;在某些情況下，伺服器端會拒絕客戶端發送的請求，因為發請求時可能會夾帶正文 (request body)，所以每次請求被拒絕都會造成頻寬上的額外浪費。在 HTTP/1 沒有機制避免這種類型的浪費，而 HTTP/1.1 的 &lt;code&gt;100 (Continue)&lt;/code&gt; 狀態碼則可以協助我們避免這種浪費。具體來說，HTTP/1.1 讓使用者端先送出一個只含有標頭、不帶內文的請求到伺服器，伺服器確認沒有問題之後，會回應狀態碼 &lt;code&gt;100 (Continue)&lt;/code&gt;；收到 &lt;code&gt;100 (Continue)&lt;/code&gt; 後，客戶端才會正式發一個帶有正文的請求；如果沒有收到，則代表伺服器端不接受該請求，這讓客戶端知道伺服器端不接受，這能讓客戶端可以避免發帶有正文的請求，進而減少傳輸上的頻寬浪費。(詳細請見 &lt;a class="link" href="https://www.rfc-editor.org/rfc/rfc2616#section-8.2.3" target="_blank" rel="noopener"
&gt;RFC 的這個段落&lt;/a&gt;)。&lt;/p&gt;
&lt;h3 id="快取緩存"&gt;快取緩存&lt;/h3&gt;
&lt;p&gt;HTTP/1 主要使用標頭中的 &lt;code&gt;If-Modified-Since&lt;/code&gt;、&lt;code&gt;Expires&lt;/code&gt; 來做為緩存的判斷標準，這兩者都是以時間作為依據；HTTP/1.1 則引入更多的緩存策略，例如：&lt;code&gt;Etag&lt;/code&gt;、&lt;code&gt;If-Unmodified-Since&lt;/code&gt;、&lt;code&gt;If-Match&lt;/code&gt;、&lt;code&gt;If-None-Match&lt;/code&gt;，透過這些可以更優化緩存的實現 (這些標頭的使用在面試也很常考，詳見&lt;a class="link" href="https://www.explainthis.io/zh-hant/interview-guides/browser/http-caching" target="_blank" rel="noopener"
&gt;這篇文章&lt;/a&gt;)。&lt;/p&gt;
&lt;h3 id="host-字段"&gt;Host 字段&lt;/h3&gt;
&lt;p&gt;HTTP/1.1 增加了 Host 字段，用來指定伺服器的域名。在 HTTP/1 中，會認為每台伺服器都綁定唯一的 IP 地址，因此請求當中的 URL 並沒有傳遞主機名(hostname)。但隨著之後虛擬主機技術的演進，現在在一台伺服器上可以存在多個虛擬主機，並且他們會共享同一個 IP 地址。所以有了 host 字段之後，就可以將請求發往同一台伺服器上的不同網站。&lt;/p&gt;
&lt;h3 id="更多請求方法"&gt;更多請求方法&lt;/h3&gt;
&lt;p&gt;HTTP/1.1 相對於 HTTP/1 新增了許多請求方法，現今我們常用的 PUT, PATCH, DELETE, CONNECT, TRACE 和 OPTIONS 等都是在 HTTP/1.1 時新增的。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="http2-和-http11-比較"&gt;HTTP/2 和 HTTP/1.1 比較&lt;/h2&gt;
&lt;h3 id="多路復用request-multiplexing-來解決頭部阻塞-head-of-line-blocking"&gt;多路復用(Request multiplexing) 來解決頭部阻塞 (head-of-line blocking)&lt;/h3&gt;
&lt;p&gt;HTTP/1.1 使用了 pipelining 的機制，這可以讓客戶端在同一個 TCP 連接內並行發出多個 HTTP 請求，客戶端也不需要等待上一次請求結果返回，就可以發出下一次請求，但伺服器端必須依照接收到的客戶端請求的先後順序一次返回，以保證客戶端能夠區分出每次請求的回應內容，但這項機制在實作上較難實現，因此各家瀏覽器，都將此功能預設為關閉。(&lt;a class="link" href="https://stackoverflow.com/questions/30477476/why-is-pipelining-disabled-in-modern-browsers" target="_blank" rel="noopener"
&gt;可以參考此篇 stackoverflow&lt;/a&gt;)。此外 pipeline 也造成頭部阻塞(head-of-line blocking ( HOL ))問題，如果有任一個請求要操作很久或傳輸包流失，那就會阻塞整個 pipeline 的工作。&lt;/p&gt;
&lt;p&gt;HTTP/2 引進了多路復用的機制，讓同一個 TCP 連接中，同時發送和接受多個請求，並且不用等到前一個請求收到回應，透過這個機制，解決了過往在 HTTP 層級的的頭部阻塞問題 (備註：但 TCP 層級仍有頭部阻塞問題，這會在 HTTP/3 被解決)。&lt;/p&gt;
&lt;h3 id="優先請求順序"&gt;優先請求順序&lt;/h3&gt;
&lt;p&gt;HTTP/2 版本中，每個請求或回應的所有數據包，稱之為一個數據流，並且，每個數據流擁有一個唯一編號 ID (&lt;code&gt;stream ID&lt;/code&gt;)。每個數據包在發送的時候就會戴上對應的數據流編號 ID，客戶端還能指定數據流的優先級，優先級越高服務器也會越快做出回應。&lt;/p&gt;
&lt;h3 id="標頭header訊息壓縮"&gt;標頭(Header)訊息壓縮&lt;/h3&gt;
&lt;p&gt;在 HTTP/2 之前因為安全性問題，多數不會對標頭的訊息進行壓縮，主要是過去的採用的演算法可能遭受 CRIME 攻擊。在 HTTP/2 中，使用 HPACK 算法來避免攻擊，進而能壓縮標頭。因為壓縮標頭，讓傳輸時能大幅減少傳輸的訊息量，進而減少頻寬負擔，也增快傳輸速度。具體上 HPACK 使用一份索引表來定義常用的 http header，並把 http header 存放在表裡，請求的時候只需要發送在表裡的索引位置即可，不須用傳完整的標頭。&lt;/p&gt;
&lt;h3 id="伺服器主動推送server-push"&gt;伺服器主動推送(Server push)&lt;/h3&gt;
&lt;p&gt;HTTP/2 允許伺服器端主動向客戶端推送數據，這能協助減少客戶端的請求次數。舉例來說，瀏覽器在過去要請求 &lt;code&gt;index.html&lt;/code&gt; 與 &lt;code&gt;style.css&lt;/code&gt; 來渲染完整的畫面；透過 Server Push，可以在瀏覽器請求 &lt;code&gt;index.html&lt;/code&gt; 時，也由伺服器主動發送 &lt;code&gt;style.css&lt;/code&gt; ，這樣只需要一輪 HTTP 的請求，就可以拿到所需的所有資源。&lt;/p&gt;
&lt;hr&gt;</description></item><item><title>初學者都該了解的 HTTP 通訊協定基礎</title><link>https://blog.wayne-project.com/2022-07-24/beginner-http-lesson/</link><pubDate>Sun, 24 Jul 2022 00:00:00 +0000</pubDate><guid>https://blog.wayne-project.com/2022-07-24/beginner-http-lesson/</guid><description>&lt;img src="https://live.staticflickr.com/65535/52276290470_787c77a1b5_o.jpg" alt="Featured image of post 初學者都該了解的 HTTP 通訊協定基礎" /&gt;&lt;style&gt;
.focus {
background: #f1e2e2;
color: #d62c2c;
padding: 0 5px;
}
&lt;/style&gt;
&lt;p&gt;&lt;a class="link" href="https://youtu.be/Taq5TV1K4XU" target="_blank" rel="noopener"
&gt;參考網站 - 初學者都該學會的 HTTP 通訊協定基礎&lt;/a&gt;&lt;br&gt;
&lt;a class="link" href="https://totoroliu.medium.com/http-2-%E6%98%AF%E4%BB%80%E9%BA%BC-d7de699bdbae" target="_blank" rel="noopener"
&gt;參考網站 - Http/2 是什麼?&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="目前-http-通訊協定的版本"&gt;目前 HTTP 通訊協定的版本&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;HTTP/0.9
&lt;ul&gt;
&lt;li&gt;於 &lt;strong&gt;1991&lt;/strong&gt; 年發表(已廢止)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HTTP/1.0
&lt;ul&gt;
&lt;li&gt;於 &lt;strong&gt;1996&lt;/strong&gt; 年 &lt;strong&gt;5&lt;/strong&gt; 月發表(&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc1945" target="_blank" rel="noopener"
&gt;RFC 1945&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HTTP/1.1
&lt;ul&gt;
&lt;li&gt;於 &lt;strong&gt;1997&lt;/strong&gt; 年 &lt;strong&gt;1&lt;/strong&gt; 月發表(&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc2068" target="_blank" rel="noopener"
&gt;RFC 2068&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;於 &lt;strong&gt;1999&lt;/strong&gt; 年 &lt;strong&gt;6&lt;/strong&gt; 月發布 HTTP/1.1 更新版(&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc2616" target="_blank" rel="noopener"
&gt;RFC 2616&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;於 &lt;strong&gt;2014&lt;/strong&gt; 年 &lt;strong&gt;6&lt;/strong&gt; 月再次更新 HTTP/1.1 並將規格文件拆成六份&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HTTP/2(&lt;a class="link" href="https://zh.wikipedia.org/zh-tw/HTTP/2" target="_blank" rel="noopener"
&gt;Wikipedia&lt;/a&gt;)
&lt;ul&gt;
&lt;li&gt;於 &lt;strong&gt;2015&lt;/strong&gt; 年 &lt;strong&gt;5&lt;/strong&gt; 月發布(&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc7540" target="_blank" rel="noopener"
&gt;RFC 7540&lt;/a&gt;)(&lt;a class="link" href="https://caniuse.com/?search=http2" target="_blank" rel="noopener"
&gt;瀏覽器相容性&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;僅針對 HTTP/1.1 的 Message Syntax 部分作出強化&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="http09-已廢止"&gt;HTTP/0.9 (已廢止)&lt;/h2&gt;
&lt;h3 id="概要"&gt;概要&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;於 &lt;strong&gt;1990&lt;/strong&gt; 年由 Tim Berners-Lee 提出最初的 &lt;strong&gt;HTTP&lt;/strong&gt; 建議(&lt;strong&gt;WWW&lt;/strong&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;實作重點&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用戶端要求是以 &lt;strong&gt;ASCII&lt;/strong&gt; 字元為主(單行命令即可發出請求 GET/)&lt;/li&gt;
&lt;li&gt;用戶端要求會以一個 &lt;strong&gt;換行字元&lt;/strong&gt; (CRLF) 來結束&lt;/li&gt;
&lt;li&gt;伺服器回應是以 &lt;strong&gt;ASCII&lt;/strong&gt; 字元為主，回應內容是 &lt;strong&gt;HTML&lt;/strong&gt; 文件&lt;/li&gt;
&lt;li&gt;每次執行完 Request / Response 就會自動關閉連線&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;主要特色&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Client / Server 架構、Request / Response 協定&lt;/li&gt;
&lt;li&gt;跑在 &lt;strong&gt;TCP/IP&lt;/strong&gt; 上的 &lt;strong&gt;ASCII&lt;/strong&gt; 協定 (要求/回應)&lt;/li&gt;
&lt;li&gt;被設計用來傳輸 &lt;strong&gt;HTML&lt;/strong&gt; 文件&lt;/li&gt;
&lt;li&gt;每完成一次 &lt;strong&gt;ASCII&lt;/strong&gt; 協定就會自動關閉連線&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="使用-bash-測試"&gt;使用 bash 測試&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 建立連線到 google&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ telnet google.com &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; Connected to google.com.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; Escape character is &lt;span class="s1"&gt;&amp;#39;^]&amp;#39;&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 發送 GET 請求&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ GET /about
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="http10"&gt;HTTP/1.0&lt;/h2&gt;
&lt;h3 id="概要-1"&gt;概要&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在 &lt;strong&gt;1991&lt;/strong&gt; 到 &lt;strong&gt;1995&lt;/strong&gt; 年之間，所謂 &lt;strong&gt;瀏覽器&lt;/strong&gt; 騰空出世！&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;於 &lt;strong&gt;1996&lt;/strong&gt; 年 &lt;strong&gt;5&lt;/strong&gt; 月發表 &lt;strong&gt;HTTP/1.0&lt;/strong&gt; 版 (&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc1945" target="_blank" rel="noopener"
&gt;RFC 1945&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;與 &lt;strong&gt;HTTP/0.9&lt;/strong&gt; 不同的部分&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用戶端要求是以 &lt;strong&gt;ASCII&lt;/strong&gt; 字元為主，但可發送多行命令(含要求標頭)
&lt;ul&gt;
&lt;li&gt;先送出&lt;strong&gt;要求方法&lt;/strong&gt;，再送出&lt;strong&gt;要求標頭&lt;/strong&gt;，最後以一個&lt;mark&gt;額外的換行字元&lt;/mark&gt;結束&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;伺服器回應是以 &lt;strong&gt;ASCII&lt;/strong&gt; 字元為主，回應內容會區分為
&lt;ul&gt;
&lt;li&gt;狀態列&lt;/li&gt;
&lt;li&gt;回應標頭&lt;/li&gt;
&lt;li&gt;回應內文主體(不僅僅是 &lt;strong&gt;HTML&lt;/strong&gt; 文件，可以是更多內容類型)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;每完成一次 Request / Response 交握就會&lt;strong&gt;自動關閉 TCP/IP 連線&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="使用-bash-測試-1"&gt;使用 bash 測試&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 建立連線到 google&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ telnet google.com &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; Trying 172.217.24.14...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; Connected to google.com.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; Escape character is &lt;span class="s1"&gt;&amp;#39;^]&amp;#39;&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 發送 GET 請求&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ GET /about HTTP/1.0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Host: www.google.com &lt;span class="c1"&gt;# 因一個 domain 可以 hosting 多個網站，因此通常都會於 request header 再標記 Host&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; HTTP/1.0 &lt;span class="m"&gt;301&lt;/span&gt; Moved Permanently
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 取得後，會自動中斷 TCP 連線&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 若需再發送請求，需重新建立 TCP 連線&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ telnet google.com &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="http11"&gt;HTTP/1.1&lt;/h2&gt;
&lt;h3 id="概要-2"&gt;概要&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;於 &lt;strong&gt;1997&lt;/strong&gt; 年 &lt;strong&gt;1&lt;/strong&gt; 月發表 &lt;strong&gt;HTTP/1.1&lt;/strong&gt; 版 (&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc1945" target="_blank" rel="noopener"
&gt;RFC 1945&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;於 &lt;strong&gt;1999&lt;/strong&gt; 年 &lt;strong&gt;6&lt;/strong&gt; 月發布 &lt;strong&gt;HTTP/1.1&lt;/strong&gt; 更新版 (&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc2616" target="_blank" rel="noopener"
&gt;RFC 2616&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;與 &lt;strong&gt;HTTP/1.0&lt;/strong&gt; 不同的部分&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;傳輸協議的效能改進(新增不少 HTTP 標頭定義)
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://en.wikipedia.org/wiki/HTTP_persistent_connection" target="_blank" rel="noopener"
&gt;持續連線狀態&lt;/a&gt;(persistent connection)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://en.wikipedia.org/wiki/HTTP_chunked_encoding_transfer" target="_blank" rel="noopener"
&gt;切塊編碼傳輸&lt;/a&gt;(chunked encoding transfer)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://en.wikipedia.org/wiki/HTTP_byte_range_request" target="_blank" rel="noopener"
&gt;位元範圍請求&lt;/a&gt;(byte range request)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://en.wikipedia.org/wiki/HTTP_cache_control" target="_blank" rel="noopener"
&gt;額外快取機制&lt;/a&gt;(cache control)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://en.wikipedia.org/wiki/HTTP_request_pipelining" target="_blank" rel="noopener"
&gt;請求管線作業&lt;/a&gt;(request pipelining)(需搭配&lt;strong&gt;持續連線狀態特性&lt;/strong&gt;才能用)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="使用-bash-測試-2"&gt;使用 bash 測試&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 建立連線到 google&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ telnet google.com &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; Trying 172.217.24.14...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; Connected to google.com.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; Escape character is &lt;span class="s1"&gt;&amp;#39;^]&amp;#39;&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 發送 GET 請求&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ GET /about HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Host: www.google.com &lt;span class="c1"&gt;# 因一個 domain 可以 hosting 多個網站，因此通常都會於 request header 再標記 Host&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; HTTP/1.0 &lt;span class="m"&gt;301&lt;/span&gt; Moved Permanently
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 取得後，不會自動中斷 TCP 連線，可再發送請求&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ GET /abc HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Host: www.google.com &lt;span class="c1"&gt;# 因一個 domain 可以 hosting 多個網站，因此通常都會於 request header 再標記 Host&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; HTTP/1.1 &lt;span class="m"&gt;404&lt;/span&gt; Page Not Found
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="目前-http11-的六大規格"&gt;目前 HTTP/1.1 的六大規格&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc7230" target="_blank" rel="noopener"
&gt;RFC 7230&lt;/a&gt; HTTP/1.1：Message Syntax and Routing&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc7231" target="_blank" rel="noopener"
&gt;RFC 7231&lt;/a&gt; HTTP/1.1：Semantics and Content(最重要)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc7232" target="_blank" rel="noopener"
&gt;RFC 7232&lt;/a&gt; HTTP/1.1：Conditional Requests&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc7233" target="_blank" rel="noopener"
&gt;RFC 7233&lt;/a&gt; HTTP/1.1：Range Requests&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc7234" target="_blank" rel="noopener"
&gt;RFC 7234&lt;/a&gt; HTTP/1.1：Caching&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc7235" target="_blank" rel="noopener"
&gt;RFC 7235&lt;/a&gt; HTTP/1.1：Authentication&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="關於-http-通訊協定的基本常識"&gt;關於 HTTP 通訊協定的基本常識&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;無狀態特性(stateless)：相同的 request，都會得到相同的 response，不會因為狀態不同而改變&lt;/li&gt;
&lt;li&gt;分散式架構(distributed)：相同的 request，不同載體發送，都會得到相同的 response&lt;/li&gt;
&lt;li&gt;協作的架構(collaborative)：不一定只有 client / server，也可以是 client / proxy / reverse proxy / server&lt;/li&gt;
&lt;li&gt;超文本(hypertext = not just &amp;ldquo;text) but with &amp;ldquo;links&amp;rdquo;)：除了內容，還包含連結&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="初學者應了解-http-的基本運作方式"&gt;初學者應了解 HTTP 的基本運作方式&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如何發出 HTTP 要求(HTTP Request)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;要求方法(Request Methods)
&lt;ul&gt;
&lt;li&gt;GET、POST、PUT、DELETE、PATCH、&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;要求標頭(Request Headers)
&lt;ul&gt;
&lt;li&gt;Connection、COntent-Type、&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;斷行符號(CRLF)&lt;/li&gt;
&lt;li&gt;要求內容主體(Request Body)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如何回應 HTTP 訊息(HTTP Response)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;回應狀態碼(Response Status Codes)
&lt;ul&gt;
&lt;li&gt;HTTP/1.1 &lt;code&gt;200&lt;/code&gt; OK&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;回應標頭(Response Headers)&lt;/li&gt;
&lt;li&gt;斷行符號(CRLF)&lt;/li&gt;
&lt;li&gt;回應內容主題(Response Body)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="http2"&gt;HTTP/2&lt;/h2&gt;
&lt;h3 id="http2-各個瀏覽器的支援度"&gt;HTTP/2 各個瀏覽器的支援度&lt;/h3&gt;
&lt;p&gt;&lt;a class="link" href="https://caniuse.com/?search=HTTP%2F2" target="_blank" rel="noopener"
&gt;點我查看瀏覽器支援度&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Edge / Firefox / Chrome / Opera 皆在 2015 年起就支援 HTTP/2，基本上不需要太擔心主流瀏覽器的支援性問題。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PS：&lt;a class="link" href="https://hieven.medium.com/http-2-%E5%BE%9E%E9%9B%B6%E5%88%B0%E4%B8%80-be221087cd35" target="_blank" rel="noopener"
&gt;如何在 Node.js 中使用 HTTP/2&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id="http2-改善了什麼"&gt;HTTP/2 改善了什麼？&lt;/h3&gt;
&lt;p&gt;http/2標準發布於2015年5月，其主要目的是透過一些措施改善瀏覽器瀏覽網頁 &lt;strong&gt;加載的速度(page load)&lt;/strong&gt; 。目前大多數的瀏覽器已支援 http/2 (chrome, firefox, safari等)標準，又 http/2與 http/1.1有著高度的相容信，舉凡request method(post, get..etc), http status code, url, header 等等，因此對於web developer來說，只需要確保你的網站有支援https(因為瀏覽器只支援https 在http/2上)。那麼http/1.1與http/2到底相差多快呢，&lt;a class="link" href="http://www.http2demo.io/" target="_blank" rel="noopener"
&gt;點我體驗&lt;/a&gt;!&lt;/p&gt;
&lt;h3 id="http2-是如何改善"&gt;HTTP/2 是如何改善？&lt;/h3&gt;
&lt;h4 id="只需要單一網路連線-single-tcp-connection"&gt;只需要單一網路連線 (Single TCP connection)&lt;/h4&gt;
&lt;p&gt;只需要單一網路連線，就可以連接網站伺服器，下載所有需要的資源。大大節省 HTTP/1.1 需要一直建立多個網路連線時的啟動時間浪費。&lt;/p&gt;
&lt;h4 id="request-and-response-multiplexing-多路複用"&gt;Request and response multiplexing (多路複用)&lt;/h4&gt;
&lt;p&gt;在 http/1.1 中，client 端時常會同時發起多個 request 至 server 拿取檔案(像是js, css, image等)，以此方式達到快速載入頁面。如下圖在 http/1.1 中會同時與 server 建立3個 TCP connection，但是瀏覽器通常會限制 TCP connection 同時建立的數目。因此在 http/2 協定中，允許 client 端與同一 server 建立單一 TCP connection 並以非同步方式傳輸要的檔案。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://live.staticflickr.com/65535/52275827083_0a977870db_o.png"
max-width="100%"
max-height="100%"
class="gallery-image"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h4 id="優先權設計-prioritization"&gt;優先權設計 (&lt;a class="link" href="https://nghttp2.org/blog/2014/04/27/how-dependency-based-prioritization-works/" target="_blank" rel="noopener"
&gt;Prioritization&lt;/a&gt;)&lt;/h4&gt;
&lt;p&gt;伺服器可以決定例如 CSS 或 JavaScript 檔案，哪些要優先傳送。&lt;/p&gt;
&lt;h4 id="header-compression-標頭壓縮"&gt;Header compression (標頭壓縮)&lt;/h4&gt;
&lt;p&gt;每一個 http 的傳輸中都會攜帶一組 header，在 http/1.1 中，header 會是以明文(plain text)傳輸大小通常會是500-800 bytes，若有攜帶 cookie 也有可能會更大。因此在 http/2 中，會將 request 以及 response 的 header 使用 &lt;a class="link" href="https://http2.github.io/http2-spec/compression.html" target="_blank" rel="noopener"
&gt;HPACK&lt;/a&gt; 演算法壓縮 header 的內容，此方法壓縮後可以減少 85%-88% 的大小。&lt;/p&gt;
&lt;h4 id="server-push-伺服器推送"&gt;Server push (伺服器推送)&lt;/h4&gt;
&lt;p&gt;在 http/1.1 中，通常 client 端 request 甚麼 server 就會回傳甚麼，例如: 當 client request html 那麼 server 將只會回傳 html。但在 http/2 中，允許 server 主動推送有相關的資料給 client，例如: 當 client 只 request html，但是 server 知道 client request 此 html 後續也會 request css, js 等，因此 server 就會在 client 沒有 request 的情況下主動推送 css, js 檔給 client。那 server 怎麼知道這些檔案是有相關性的呢? web developer 將需要 server push 的檔案加上特定的描述即可。(&lt;a class="link" href="https://www.smashingmagazine.com/2017/04/guide-http2-server-push/" target="_blank" rel="noopener"
&gt;描述&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://live.staticflickr.com/65535/52274831682_c7dd69e454_o.png"
max-width="100%"
max-height="100%"
class="gallery-image"
loading="lazy"
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;不過，這個功能比較有爭議，一來他需要 Web 開發者額外描述有哪些檔案需要隨著 HTML 一起推送給瀏覽器，不是 Web 伺服器升級 HTTP/2 就自動會有。二來它不管瀏覽器是不是已經有快取這個資源，都會推送而造成頻寬浪費。因此實務上筆者認為可以改用瀏覽器的 &lt;a class="link" href="https://css-tricks.com/prefetching-preloading-prebrowsing/" target="_blank" rel="noopener"
&gt;Prefetch&lt;/a&gt; 功能，讓客戶端的瀏覽器自己處理即可。&lt;/p&gt;&lt;/blockquote&gt;
&lt;h4 id="binary-framing-layer-二進制影格層"&gt;Binary framing layer (二進制影格層)&lt;/h4&gt;
&lt;p&gt;在 http/2 中，header 與 body 所挾帶的 property 與 http/1.1 相同(ex. verbs, methods)，然而兩者在傳輸上會有不同。在 http/2 中，會將 header 以及 body 編碼成二進制在 server 與 client 端中傳輸，在 http/1.1 中，則是以明文的方式傳輸。將訊息編碼成二進制進行傳輸，此特性是 http/2 的其他特性的根本基礎。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;冷知識：在 HTTP/1.1 定義了&lt;a class="link" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4" target="_blank" rel="noopener"
&gt;四種解析訊息的方式&lt;/a&gt;，在 HTTP/2 只需要一種。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://live.staticflickr.com/65535/52274831672_3c70d84aab_o.png"
max-width="100%"
max-height="100%"
class="gallery-image"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h3 id="總結"&gt;總結&lt;/h3&gt;
&lt;p&gt;根據 &lt;a class="link" href="https://w3techs.com/technologies/details/ce-http2" target="_blank" rel="noopener"
&gt;w3Techs&lt;/a&gt; 網站，目前使用http/2的網站有45.7%，未來確實會變成主流，身為軟體工程師，還是多少理解來的好。此篇文章僅記錄筆者蒐集資料彙整結果，若有任何錯誤資訊還請直接點出，萬分感謝。&lt;/p&gt;
&lt;hr&gt;</description></item></channel></rss>