1. 程式人生 > 實用技巧 >SSL/TLS協議詳解(下)——TLS握手協議

SSL/TLS協議詳解(下)——TLS握手協議

本文轉載自SSL/TLS協議詳解(下)——TLS握手協議

導語

在部落格系列的第2部分中,對證書頒發機構進行了深入的討論.在這篇文章中,將會探索整個SSL/TLS握手過程,在此之前,先簡述下最後這塊內容的關鍵要點:

  • TLS適用於對稱金鑰
  • 對稱金鑰可以通過安全金鑰交換演算法共享
  • 如果請求被截獲,金鑰交換可能會被欺騙
  • 使用數字簽名進行身份驗證
  • 證書頒發機構和信任鏈。

在這篇文章中,使用WireShark的工具來檢視網路流量,我個人使用的是Linux(Ubuntu 16.04)系統,可以使用以下命令輕鬆安裝WireShark:

$sudo apt install wireshark

以sudo的許可權開啟WireShark並選擇提供網際網路連線的介面,我這裡是eth0,然後點選WireShark右上角的“開始捕獲資料包”按鈕,Wireshark將立即開始抓取通過機器的所有流量。現在我們從瀏覽器中載入

github.com。Github使用TLS進行所有通訊,將重新定向到https並載入。現在關閉瀏覽器,看看WireShark抓到了什麼。

DNS解析

這並不是TLS的一部分,但我們在WireShark中看到了它。

  我已將Google DNS設定為我的DNS伺服器,它的地址是8.8.8.8,在影象中可以看到請求已傳送到8.8.8.8查詢github.com的A記錄,也就是我們想要連線的Github的IP地址。

  DNS伺服器使用github.com的IP響應為192.30.253.113,藍色表示選擇顯示相應的部分,現在,瀏覽器已獲取了將要用來連線伺服器的目標IP。

發起TLS握手

解析IP後,瀏覽器將通過http請求頁面,如果伺服器支援TLS,那麼它將傳送協議升級請求來響應瀏覽器,這個新的地址

https://github.com ,將使用埠號443來指定,隨後瀏覽器將啟動TLS握手請求。大多數現代瀏覽器都存有與Web伺服器的最後一次連線的記錄,如果最後一次連線是通過https進行的,那麼下次瀏覽器將自動啟動https請求而無需等待伺服器。
TLS握手分為以下幾個步驟:

  • 客戶端傳送Hello報文
  • 伺服器接收Hello報文
  • 共享證書和伺服器金鑰交換
  • 更改密碼規範
  • 加密握手

客戶端傳送Hello報文

從這裡開始,我將會重點討論圖片中標記為藍色的主題,Client傳送的Hello報文如下圖所示。

  我們知道TLS是在TCP之上實現的協議,TLS本身是一層協議並且它的底層叫做記錄協議(Record protocol)

,這意味著所有資料都被記錄。典型的記錄格式如下:

HH V1:V2 L1:L2 data
  • HH是單個位元組,表示記錄中的資料型別。共定義了四種類型:change_cipher_spec(20),alert(21),handshake(22)和application_data(23)。
  • V1:V2是協議版本,用兩個以上的位元組表示。對於當前定義的所有版本,V1的值為0x03,而對於SSLv3,V2的值為0x00,對於TLS 1.0為0x01,對於TLS 1.1為0x02,對於TLS 1.2為0x03。
  • L1:L2是資料的長度,以位元組為單位(使用big-endian約定:長度為256 * L1 + L2),資料的總長度不能超過18432位元組,但實際上它無法達到這個值。

在圖中,可以看出內容型別是Handshake,TLS版本1.0,資料長度為512.真實資料位於名為 Handshake Protocol:Client Hello的下拉列表中。我們繼續觀察下Client Hello中共享的資料。

客戶端傳送Hello報文的內容

瀏覽器與伺服器共享以下詳細資訊

客戶端版本

按優先順序列出的客戶端支援的協議版本,首選客戶希望支援的最新協議版本。

客戶端的隨機數

一個32位元組的資料,其中前4個位元組表示epoch格式的當前日期時間。紀元時間是自1970年1月1日以來的秒數。其餘28個位元組由加密強隨機數生成器生成(例如,Linux中的/dev/urandom),客戶端隨機會在後面用到,請先記住這點。

會話id(Session id)

如果客戶端第一次連線到伺服器,那麼這個欄位就會保持為空。在上圖中,您可以看到Session id正在給伺服器傳送東西,之所以會發生這種情況是由於我之前是通過https連線到github.com的,在此期間,伺服器將使用Session id對映對稱金鑰,並將Session id儲存在客戶端瀏覽器中,為對映設定一個時間限。如果瀏覽器將來連線到同一臺伺服器(當然要在時間限到期之前),它將傳送Session id,伺服器將對對映的Session進行驗證,並使用以前用過的對稱金鑰來恢復Session,這種情況下,就不需要完全握手。

密碼套件

客戶端還將傳送自己已經知道的密碼套件列表,這個是由客戶按優先順序排列的,但完全由伺服器來決定傳送與否。TLS中使用的密碼套件有一種標準格式。

我們從列表中用一個例子來進行分析.

Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
  • TLS:指使用的協議是TLS
  • ECDHE:金鑰交換演算法
  • ECDSA:簽名或驗證演算法
  • AES_128_GCM:稱為批量加密演算法。對稱金鑰加密演算法是AES,金鑰長度為128位,AES是塊密碼,也就是對輸入的純文字用固定長度的塊來進行加密,加密後的每個塊按再順序傳送,最後按類似的方式來進行解密。按標準規定,AES塊固定長度為128位,但是輸入的明文不要求必須是128的倍數,所以我們可能需要對最後一個塊中進行填充,使其為固定的長度128位。除此之外,為了提高平均資訊量,通常在加密之前會新增一些隨機的位元到明文中,我們稱為初始化向量(IV)。有很多演算法都可以在塊上新增IV實現填充。在我們的例子Galois/Counter Mode(GCM)中用到過。或許詳細解釋GCM模式會使事情變得複雜,可見這並不是一個好主意。
    SHA256:訊息驗證程式碼(MAC)演算法。我們將詳細討論MAC。

壓縮資料

為了減少頻寬,可以進行壓縮。但從成功攻擊TLS的事例中來看,其中使用壓縮時的攻擊可以捕獲到用HTTP頭髮送的引數,這個攻擊可以劫持Cookie,這個漏洞我們稱為CRIME。從TLS 1.3開始,協議就禁用了TLS壓縮。

副檔名

其他引數(如伺服器名稱,填充,支援的簽名演算法等)可以作為副檔名使用,我們可以任意對用作副檔名的內容研究一番。

這些是客戶端問候的一部分,如果已收到客戶端問候,接下來就是伺服器的確認,伺服器將傳送伺服器問候。

伺服器接收Hello報文

收到客戶端問候之後伺服器必須傳送伺服器問候資訊,伺服器會檢查指定諸如TLS版本和演算法的客戶端問候的條件,如果伺服器接受並支援所有條件,它將傳送其證書以及其他詳細資訊,否則,伺服器將傳送握手失敗訊息。

圖中,我們可以看到伺服器響應0x0303表示伺服器同意使用TLS 1.2,我們來檢查一下伺服器問候中的記錄。

伺服器接收Hello報文的內容

伺服器問候訊息包含以下資訊。

加下來我們會在這裡討論其中一些重要的引數。

伺服器版本

如果客戶端可以支援,則伺服器將選擇客戶端指定的TLS版本,這裡選擇了TLS 1.2

伺服器的隨機數

類似於客戶端隨機,伺服器隨機也佔32位元組,前4個位元組表示伺服器的Unix紀元時間,後面加上28位元組的隨機數。客戶端和伺服器隨機將用來建立加密金鑰,我待會兒會解釋。

密碼套件

還記得我們已經將傳送支援的密碼套件傳送到客戶端問候中的github.com嗎?Github從名單中選出了第一個,也就是:

TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256

會話id(Session id)

伺服器將約定的Session引數儲存在TLS快取中,並生成與其對應的Session id。它與Server Hello一起傳送到客戶端。客戶端可以寫入約定的引數到此Session id,並給定到期時間。客戶端將在Client Hello中包含此id。如果客戶端在此到期時間之前再次連線到伺服器,則伺服器可以檢查與Session id對應的快取引數,並重用它們而無需完全握手。這非常有用,因為伺服器和客戶端都可以節省大量的計算成本。

在涉及亞馬遜和谷歌等流量巨大的應用程式時,這種方法存在缺點。每天都有數百萬人連線到伺服器,伺服器必須使用Session金鑰保留所有Session引數的TLS快取。這是一個巨大的開銷。為了解決之前介紹過的Session Tickets的問題, 在這裡,客戶端可以在client hello中指定它是否支援Session Ticket。然後,伺服器將建立一個新的會話票證(Session Ticket),並使用只有伺服器知道的經過私鑰加密的Session引數。它將儲存在客戶端上,因此所有Session資料僅儲存在客戶端計算機上,但Ticket仍然是安全的,因為該金鑰只有伺服器知道。

此資料可以作為名為Session Ticket的擴充套件包含在Client Hello中。在我們的例子中,此引數為空,因為這是第一次連線到github.com或前一個Session的瀏覽器已過期。

壓縮資料

如果支援,伺服器將同意客戶端的首選壓縮方法。在這裡,您可以看到伺服器響應為空響應,則意味著不需要壓縮。

伺服器不在ServerHello訊息中傳送任何證書; 它會在正確命名的證書訊息中傳送證書。

伺服器證書的資訊


  在我們的例子中,證書訊息長度為3080位元組。毫無疑問,這是包含所有資訊的伺服器證書。伺服器按信任鏈的順序傳送完整的證書列表。該鏈中的第一個是伺服器證書,接著是頒發伺服器證書的intermediate CA 的證書,然後是下一個intermediate CA 的證書......直到Root CA的證書。伺服器不可以傳送Root CA證書,因為在大多數情況下,瀏覽器可以從任何intermediate CA 識別Root CA。

在我們的例子中,您可以看到第一個證書是github.com,第二個證書是中介軟體Digicert SHA2擴充套件驗證Server CA。 檢查下圖中的id-at-commonName引數。

讓我們分析證書的內容,看看瀏覽器如何驗證它。

證書的內容

證書被髮送到瀏覽器,因此我們可以在訪問github.com時檢視Github的證書。來自Firefox的CA證書內容:

可以通過單擊"詳細資訊"選項卡檢視github的intermediate CA 和Root CA.

讓我們瞭解這些領域是什麼以及它們的用途。

版本和序列號

版本表示使用的是哪個版本的X.509標準。X.509是用於定義公鑰證書格式的標準。X.509有3個版本,github使用最新版本version 3。

從RFC 5280開始,CA為每個證書分配的序列號必須是正整數。因此對於每個釋出CA證書,它必須是唯一的(即頒發者名稱和序列號標識唯一的證書)。所以,CA必須強制serialNumber為非負整數。

證書的簽名演算法與值

瀏覽器需要知道簽名演算法以驗證簽名。如果使用的是RSA簽名,則需要相同的演算法來驗證簽名。對於Github,使用的是PKCS#1 SHA-256和RSA加密,即SHA-256用於生成雜湊,RSA用於簽名。

從我們上一篇文章中,證書資料使用SHA-256演算法進行雜湊處理,並使用RSA加密過Github的私鑰對此雜湊進行簽名。

頒佈機構

此欄位包含頒發證書的頒發機構的詳細資訊。Github的證書由Digicert的intermediate CA 頒發。

合法性

該欄位有兩個值Not BeforeNot After 。如果當前日期時間不在這些值之間,則證書無效。瀏覽器就不會信任該證書。

子公鑰資訊(Subject Public Key Info)

該欄位攜帶公鑰和用於生成公鑰的演算法。此金鑰用於交換金鑰,我們將在稍後討論。

指紋

瀏覽器生成了兩個指紋SHA 1和SHA-256,而且不會發送到伺服器。這些指紋分別是通過SHA 1和SHA-256函式雜湊DER格式的證書產生的。我們可以通過將證書下載到我們的機器並應用雜湊函式來驗證這一點。

單擊詳細資訊選項卡左下角的“ 匯出” 按鈕以下載證書,儲存為.crt 副檔名,並在終端上執行以下命令以生成證書的指紋。

$ openssl x509 -noout -fingerprint -sha256 -inform pem -in [certificate-file.crt]

$ openssl x509 -noout -fingerprint -sha1 -inform pem -in [certificate-file.crt]

這應該產生與您在瀏覽器中看到的結果相同的結果。這些值不是證書的一部分,而是根據證書計算出來的。Root CA證書的指紋將在瀏覽器中進行硬編碼,因此可以輕鬆地進行交叉驗證。除此之外,這些指紋主要用於識別和組織證書。不要將Signature與指紋混淆

我們在這裡討論的證書資訊是關於github.com的伺服器證書。Github的intermediate CA 證書也將在同一請求中傳送給客戶,所有上述欄位也適用於該證書。您可以通過轉到詳細資訊選項卡並單擊intermediate CA 來檢查,如下所示。

伺服器端金鑰交換

隨後是Server Hello和證書訊息(Certificate message),伺服器金鑰交換(Server Key Exchange)是可選的。僅當伺服器提供的證書不足以允許客戶端交換預主金鑰時,才會傳送此訊息。讓我們看看為什麼github.com必須傳送伺服器金鑰交換訊息。

我們可以看到github.com首選Session的密碼套件是TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256。這意味著雙方使用Elliptic Curve Diffie Hellman演算法來交換金鑰。在Diffie-Hellman中,客戶端無法自行計算預主金鑰; 雙方都有助於計算它,因此客戶端需要從伺服器獲取Diffie-Hellman公鑰。(不要對"Pre-Master Secret"一詞感到困惑,我們將在下面深入討論它。)當使用Elliptic Curve Diffie-Hellman時,該公鑰不在證書中。因此,伺服器必須在單獨的訊息中向客戶端傳送其DH公鑰,以便客戶端可以計算預主金鑰。這可以在上面的影象中看到。請注意,此金鑰交換也由簽名保護。

伺服器金鑰交換完成後,伺服器將傳送Server Hello Done 訊息。客戶端將開始計算Pre-Master Secret。我們來看看如何。

如何計算Pre-Master Secret

Pre-Master Secret計算取決於商定的金鑰交換演算法的型別。當使用RSA進行金鑰交換時,從客戶端(即瀏覽器)計算預主金鑰,客戶端通過連線協議版本(2個位元組)和客戶端隨機生成的一些位元組(46個位元組)來生成48位元組的預主金鑰。客戶端從加密安全的偽隨機數發生器(PRNG)獲得這46個位元組。實際上,這意味著使用作業系統提供的PRNG,例如/dev/urandom。然後,使用伺服器的公共和共享對此Pre-Master金鑰進行加密,以便伺服器稍後可以使用它來建立主金鑰

但是,在Github的情況下,如上所述,Diffie-Hellman演算法用於金鑰交換。這裡的情況略有不同。伺服器立即生成一對DH私鑰 - 公鑰。然後,與客戶共享公鑰。這是如上所述的"伺服器金鑰交換訊息( Server Key Exchange)"。作為響應,客戶端還將建立DH金鑰對,並通過客戶端金鑰交換訊息與伺服器共享公鑰,如下所示。

您可以看到共享的客戶端公鑰。現在,如果您瞭解Diffie-Hellman演算法的工作原理,您就知道客戶端和伺服器可以從這些共享公鑰到達公共金鑰。新生成的金鑰稱為Pre-Master金鑰。

使用Diffie Hellman演算法進行TLS金鑰交換具有優勢。客戶端和伺服器都為每個新會話生成一個新金鑰對。一旦計算出預主金鑰,將立即刪除客戶端和伺服器的私鑰。這意味著私鑰永遠不會被竊取,確保完美的前向保密

客戶端金鑰交換

我們已經在上面討論過,客戶端的DH公鑰通過客戶端金鑰交換訊息共享給伺服器。但是如果使用RSA,則客戶端將如上所述通過其自己計算預主金鑰,使用伺服器的公鑰(RSA公鑰)對其進行加密,並通過客戶端金鑰交換訊息將其傳送回伺服器。然後,伺服器可以使用其私鑰解密它。無論演算法是什麼,此時客戶端和伺服器都達到了共同的Pre-Master Secert 。完成此操作後,客戶端將傳送Change Cipher Spec 訊息,如下所示。

讓我們往下走,看看如何在主金鑰從預備主金鑰來計算。

如何計算主祕鑰

現在客戶端和伺服器都有哪些隨機資料呢?根據RFC 5346標準,在問候訊息期間客戶端和伺服器共享的預主金鑰和隨機值(還記得嗎?)都會使用PRF(偽隨機函式)產生的值來計算主金鑰。

master_secret = PRF(pre_master_secret,“master secret”,ClientHello.random + ServerHello.random)[0..47];

這裡,

pre_master_secret - 雙方計算的48位元組Pre-Master密碼。

“master secret” - 它只是一個使用ASCII位元組的字串。

ClientHello.random - 客戶端hello中共享的隨機值

ServerHello.random - 伺服器hello中共享的隨機值。

主金鑰的大小共48個位元組,好吧,到目前為止還不是太亂。雙方都可以使用主金鑰加密資料並來回傳送,確實如此,但程式還沒結束。你認為雙方使用相同的祕鑰是個好辦法嗎?當然不是!TLS為客戶端和伺服器分配了單獨的金鑰,它們都來自主金鑰本身,換句話說,主金鑰不直接用於加密資料,而是將單獨的加密金鑰用於客戶端和伺服器。由於雙方都有兩個金鑰,伺服器用其金鑰加密的資料可以由客戶端輕鬆解密,反之亦然。

還沒完,TLS還具有用於對稱金鑰加密的附加安全機制。

訊息驗證程式碼(MAC)和TLS資料完整性

竊聽者可以對傳輸中的加密資料進行兩種可能的攻擊:嘗試解密資料或嘗試修改資料。只要金鑰安全,我們就可以認為解密基本上是不可能的,但如果是修改資料呢?客戶端和伺服器是怎麼知道攻擊者沒有修改過資料呢?如上所述,TLS不僅僅是加密資料,還可以保護資料,使其免受未檢測到的修改,換句話說,TLS可以檢查資料的完整性。讓我們看看它是怎麼做到的。

當伺服器或客戶端使用主金鑰加密資料時,它還會計算明文資料的校驗和(雜湊值),這個校驗和稱為訊息驗證程式碼(MAC)。然後在傳送之前將MAC包含在加密資料中。金鑰用於從資料中生成MAC,以確保傳輸過程中攻擊者無法從資料中生成相同的MAC,故而MAC被稱為HMAC(雜湊訊息認證碼)。另一方面,在接收到訊息時,解密器將MAC與明文分開,然後用它的金鑰計算明文的校驗和,並將其與接收到的MAC進行比較,如果匹配,那我們就可以得出結論:資料在傳輸過程中沒有被篡改。

客戶端和伺服器必須使用相同的雜湊演算法來建立以及驗證MAC,還記得Github同意的密碼套件的最後一部分嗎?
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_ SHA256。即SHA256 是用於處理HMAC的雜湊函式,為了提高安全性,客戶端和伺服器使用MAC金鑰。讓我們看看這些是什麼。

MAC金鑰和IV金鑰

根據要求,有4個金鑰用於加密和驗證每個訊息的完整性,他們是:

  • 客戶端寫入加密金鑰:客戶端用賴加密資料,伺服器用來解密資料。
  • 伺服器寫入加密金鑰:伺服器用來加密資料,客戶端用來解密資料。
  • 客戶端寫入MAC金鑰:客戶端用來建立MAC,伺服器用來驗證MAC。
  • 伺服器寫入MAC金鑰:伺服器用來建立MAC,客戶端用來驗證MAC。

這些金鑰塊由主金鑰上的相同的PRF反覆地生成,直至金鑰有了足夠的位元組。

key_block = PRF(SecurityParameters.master_secret,“金鑰擴充套件”,SecurityParameters.server_random + SecurityParameters.client_random);

如您所見,除了客戶端 - 伺服器隨機值和字串“金鑰擴充套件”之外,主金鑰還用來增加金鑰的平均資訊量。PRF可以生成任意長度的金鑰,這點是很有用的,因為預設情況下不同的雜湊函式具有不同的長度。在我們的例子中用的是SHA256,它是256位,但MD5的預設長度為128位。

除此之外,我們知道我們使用的AES和GCM演算法是一種分組密碼,它需要一組位元來作為初始化向量(IV)。在討論密碼套件時,我們已經提到IV用於改善AES加密的平均資訊量,換句話說,當多次加密同一檔案時,IV能夠生成不同的密文,這些隨機的位元組也由相同的PRF生成,並且被稱為客戶端寫入IV 和伺服器寫入IV ,術語是自解釋的。我不會對IV的細節再進行更多講解,因為它是一個很大的主題,超出了本文的範圍。

生成測試資料

現在雙方都有了加密金鑰,我們準備加密,但是在將TLS放到應用層之前,我們需要像每個程序一樣來測試並驗證客戶端加密資料是否可以由伺服器解密,反之亦然。為此,客戶端將使用偽隨機函式(PRF)計算12位元組的verify_data,如下所示。

verify_data = PRF(master_secret, "client finished", MD5(handshake_messages) + SHA-1(handshake_messages) ) [12]

其中handshake_messages 是所有握手訊息的緩衝區,以上版本適用於版本1.2的TLS。版本1.2略有變化,即verify_data的長度取決於密碼套件而不總是12位元組,任何未明確指定verify_data_length的密碼套件都等於12。此外,偽隨機函式(PRF)中的MD5 / SHA-1組合具有已被密碼套件指定的PRF替換。所以根據最新規範,

Verify_data = PRF(master_secret, finished_label, Hash(handshake_messages)) [0..verify_data_length-1];

因此我們有測試資料,用金鑰和演算法來加密測試資料。客戶端所需要做的就是用客戶端加密金鑰(或簡稱客戶端寫入金鑰)使用AES演算法加密測試資料,如上所述還得計算HMAC,客戶端獲取結果並新增記錄頭位元組“0x14”表明“已完成”,再通過客戶端生成訊息並且傳送到伺服器。這是由實體和客戶端傳送的最後一次握手訊息之間協商的演算法和金鑰保護的第一條訊息。由於訊息是完全加密的,因此WireShark只會看到加密的內容,並通過名稱為加密握手的訊息來呼叫完成的握手資訊,如下所示。

驗證磋商

伺服器處理過程也幾乎相同。它發出一個Change Cipher Spec ,然後傳送一條包含所有握手訊息的已完成資訊。更改標記在該伺服器切換到新協商的加密套件和鍵點的密碼SPEC訊息,然後再加密後續客戶端的記錄。除此之外,伺服器的完成訊息將包含對客戶端的完成訊息進行解密的版本,一旦客戶端收到此資料,它將使用伺服器寫入金鑰對其進行解密。故而這就向客戶證明了伺服器能夠成功解密我們的訊息。KABOOM!我們完成了TLS握手。

所有的加密都是基於協商的演算法。在我們的例子中,演算法是AES_128_GCM,這裡沒有必要進行進一步的解釋,因為當涉及到其他網站時,伺服器指定的演算法可能會有所不同。如果您有興趣瞭解這些演算法的工作原理,維基百科有一個列表。我也是通過TLS基礎知識來學習密碼學。

加密應用程式資料

我們現在在應用層。如果你有一箇中等速度的網際網路連線,我們只是連線了幾百毫秒。想象一下,在如此短的時間內會發生多少事情?

我要求的頁面是homepade aka www.github.com

。所以在Mozilla Firefox的開發者工具中顯示的純文字請求是,

GET https://github.com/

Host: github.com

User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: en-US,en;q=0.5

Accept-Encoding: gzip, deflate, br

Connection: keep-alive

Upgrade-Insecure-Requests: 1

Cache-Control: max-age=0

請參閱下圖:

前3個位元組17 03 03 表示內容的資料型別(應用程式資料)和TLS版本(TLS 1.2)。

尾聲

對,就是這樣。我們結束了。

在本系列的下一部分中,我會新增一些在本文中無法包含的別的內容。我還發布了結構化的參考連結,這對於學習TLS中的密碼學還是很有用的。
  我想在這裡再寫點什麼。整篇文章寫的都是我對TLS的理解上的興趣,就是說我所學到/理解的一切都在這裡了,或許並不完整,或許會有錯誤,或許各位的看法和我有出入。總之,不管是什麼,歡迎您在評論區中分享,我很高興能和諸位一起學習更多的東西!