為什麼 Apache 在使用“openssl s_client”連接到它時返回 400“錯誤請求”錯誤,即使它發送 TLS SNI
我有一個網站,可以通過 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: 標頭。