Ssl

為什麼 Apache 在使用“openssl s_client”連接到它時返回 400“錯誤請求”錯誤,即使它發送 TLS SNI

  • January 10, 2022

我有一個網站,可以通過 HTTP over TLS 訪問。它是多宿主的,因此主機名有多個 A DNS 條目。此外,有幾個網站,有幾個主機名,在同一組 IP 地址上,所以這都是在 Apache 2 中使用幾個 NameVirtualHost 設置的。

使用瀏覽器或 WebDAV 客戶端等訪問網站就像一個魅力。

但是,當我嘗試連接“openssl s_client”時,我一輩子都無法讓它工作。Apache 向我拋出 400“錯誤請求”錯誤。Apache 選擇了正確的 X.509 證書,因此 openssl 必須已正確設置 SNI。一旦我在“GET”行上按“enter”,就會發生這種情況,甚至在我可以添加任何標題之前。我使用的 HTTP 語法(基本上“GET / HTTP/1.1\nHost: hostname”適用於網站的非 TLS 版本(僅重定向到 TLS 版本),所以這不應該是問題。相同的 HTTP 語法也可以通過 TLS(埠 443)連接到例如www.google.com,所以它看起來又是正確的。

Apache 伺服器的日誌顯示帶有尾隨換行符的請求,這可能是一個提示。儘管 Apache 為我連接的主機名發送了 X.509 證書,但日誌條目轉到該 IP/埠的預設VirtualHost 條目,這與預設值不同。

我已經與wireshark 確認在ClientHello 中設置了SNI。

這是一個範例會話:

輸入 $ openssl s_client -connect hostname:443

輸出

depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = hostname
verify return:1
---
Certificate chain
0 s:CN = hostname
  i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
1 s:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
  i:O = Digital Signature Trust Co., CN = DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
(...)
-----END CERTIFICATE-----
subject=CN = hostname

issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 3121 bytes and written 388 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
   Protocol  : TLSv1.3
   Cipher    : TLS_AES_256_GCM_SHA384
   Session-ID: FBFD6172E3C659D4071B68A36BD5983A8571906C00190A745B5DAA1359D4087C
   Session-ID-ctx: 
   Resumption PSK: 21957A3C267DAA25CE2D7F6BFD3C3593CCFA1E6FC45C4DC5D0CE35C29175F7E9BBDD59EE6C213DC53B8291B7BD055238
   PSK identity: None
   PSK identity hint: None
   SRP username: None
   TLS session ticket lifetime hint: 300 (seconds)
   TLS session ticket:
   0000 - 18 4b 25 ae 49 61 e8 f3-73 b5 bb 3b 50 2f 1d c4   .K%.Ia..s..;P/..
   0010 - 7e 02 e9 41 01 9e ed 8b-a1 b6 ec 48 4d 12 11 f2   ~..A.......HM...

   Start Time: 1602857943
   Timeout   : 7200 (sec)
   Verify return code: 0 (ok)
   Extended master secret: no
   Max Early Data: 0
---
read R BLOCK
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
   Protocol  : TLSv1.3
   Cipher    : TLS_AES_256_GCM_SHA384
   Session-ID: 627DF7810556DB2918484F393E66DEA7BF22DD3A42288B47E46FCDCDEF176125
   Session-ID-ctx: 
   Resumption PSK: 851978077C93284CB3D69B541B77844A48D1CFC8800F60B6F7CF3D297911D0D93E0E935017B78777E2BD51A29E13329F
   PSK identity: None
   PSK identity hint: None
   SRP username: None
   TLS session ticket lifetime hint: 300 (seconds)
   TLS session ticket:
   0000 - c0 1a 60 23 36 80 bf 27-83 0e 1f 26 4b 04 f2 53   ..`#6..'...&K..S
   0010 - a0 8a 2d 6e 41 44 17 c7-9b 11 5d dc 7f 08 71 fc   ..-nAD....]...q.

   Start Time: 1602857943
   Timeout   : 7200 (sec)
   Verify return code: 0 (ok)
   Extended master secret: no
   Max Early Data: 0
---
read R BLOCK

INPUT(注意第一個之後沒有空行) GET / HTTP/1.1

輸出

Date: Fri, 16 Oct 2020 14:19:11 GMT
Server: Apache/2.4.38 (Debian)
Content-Length: 322
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.4.38 (Debian) Server at default_hostname Port 443</address>
</body></html>
closed

注意:錯誤消息提到 default_hostname,而不是主機名!

相應的 Apache 日誌條目是: default_hostname:443 1.2.3.4 - - [16/Oct/2020:16:19:11 +0200] "GET / HTTP/1.1\n" 400 3279 "-" "-"

請注意,default_hostname 不是主機名,並且 Apache 發送了正確的伺服器證書,因此它辨識出了正確的 VirtualHost!如果我在沒有 SNI 的情況下連接(openssl s_client -connect 4.3.2.1:443),那麼它會發送 default_hostname 的伺服器證書。

… “GET / HTTP/1.1\n” 400 3279 “-” “-”

以 HTTP 請求結尾的行應該是\r\n而不是\n。嘗試將Enter 翻譯成而不是 only的-crlf選項。s_client``\r\n``\n

此外,伺服器可能對完全發送請求的時間有一定的限制,而且它可能不夠快,不能只鍵入所有內容。嘗試從文本文件中剪切+粘貼預先創建的請求。

您確實發送了格式錯誤的請求。HTTP/1.1 要求存在 Host: 標頭,而且對於 SNI,Host: 標頭的內容必須與 SNI 主機名匹配。

再試一次,這次提供正確的 Host: 標頭。

引用自:https://serverfault.com/questions/1038981