在我现在正在读的一本书中,作者展示了HTTP标头的含义。也就是说,他说有托管多个网站的服务器。
让我们这样做:
ping fideloper.com
我们可以看到IP地址:198.211.113.202。
现在让我们仅使用 IP 地址:
curl -I 198.211.113.202
我们抓住:
$ curl -I 198.211.113.202
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Thu, 03 Aug 2017 14:48:33 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Location: https://book.serversforhackers.com/
接下来,让我们看看当我们向 HTTP 请求添加主机标头时会发生什么:
$ curl -I -H "Host: fideloper.com" 198.211.113.202
HTTP/1.1 200 OK
Server: nginx
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
Cache-Control: max-age=86400, public
Date: Thu, 03 Aug 2017 13:23:58 GMT
Last-Modified: Fri, 30 Dec 2016 22:32:12 GMT
X-Frame-Options: SAMEORIGIN
Set-Cookie: laravel_session=eyJpdiI6IjhVQlk2UWcyRExsaDllVEpJOERaT3dcL2d2aE9mMHV4eUduSjFkQTRKU0R3PSIsInZhbHVlIjoiMmcwVUpNSjFETWs1amJaNzhGZXVGZjFPZ3hINUZ1eHNsR0dBV1FvdE9mQ1RFak5IVXBKUEs2aEZzaEhpRHRodE1LcGhFbFI3OTR3NzQxZG9YUlN5WlE9PSIsIm1hYyI6ImRhNTVlZjM5MDYyYjUxMTY0MjBkZjZkYTQ1ZTQ1YmNlNjU3ODYzNGNjZTBjZWUyZWMyMjEzYjZhOWY1MWYyMDUifQ%3D%3D; expires=Thu, 03-Aug-2017 15:23:58 GMT; Max-Age=7200; path=/; httponly
X-Fastcgi-Cache: HIT
这意味着 serversforhackers.com 是默认站点。
然后作者说我们可以在同一台服务器上请求黑客服务器:
$ curl -I -H "Host: serversforhackers.com” 198.211.113.202
在这本书中,HTTP/1.1 200 OK被接收。
但我收到这个:
curl -I -H "Host: serversforhackers.com" 198.211.113.202
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Thu, 03 Aug 2017 14:55:14 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Location: https://book.serversforhackers.com/
好吧,作者组织了一个 301 重定向,现在使用 HTTPS。
我可以这样做:
curl -I https://serversforhackers.com
但这并不能说明默认站点是什么以及主机标头如何寻址共享 IP 地址上的特殊站点的全部概念。
是否仍有可能以某种方式通过 IP 地址获得 200 Ok 寻址?
在 HTTP/1.1 中,如果没有 HTTPS,Host
标头是主机名发送到服务器的唯一位置。
有了HTTPS,事情就更有趣了。
首先,您的客户端通常会尝试根据预期名称检查服务器的 TLS 证书:
$ curl -I -H "Host: book.serverforhackers.com" https://198.211.113.202
curl: (51) SSL: certificate subject name (book.serversforhackers.com) does not match target host name '198.211.113.202'
大多数客户端都提供了一种覆盖此检查的方法。 curl 具有-k
/--insecure
选项:
$ curl -k -I -H "Host: book.serverforhackers.com" https://198.211.113.202
HTTP/1.1 200 OK
Server: nginx
[...]
但接下来是第二个问题。我无法用您的示例服务器来说明它,但这是我在互联网上找到的:
$ curl -k -I https://analytics.usa.gov
HTTP/1.1 200 OK
Content-Type: text/html
[...]
$ host analytics.usa.gov | head -n 1
analytics.usa.gov has address 54.240.184.142
$ curl -k -I -H "Host: analytics.usa.gov" https://54.240.184.142
curl: (35) gnutls_handshake() failed: Handshake failed
这是由服务器名称指示 (SNI( 引起的,这是 TLS (HTTPS( 的一项功能,主机名也在TLS 握手中发送。这是必要的,因为服务器需要提供正确的证书(用于正确的主机名(,然后才能接收任何 HTTP 标头。在上面的例子中,当我们使用https://54.240.184.142
时,curl 不会发送正确的 SNI,并且服务器拒绝握手。其他服务器可能会接受连接,但将其路由到错误的位置,最终将忽略Host
标头。
使用 curl,您不能像设置Host
标头那样使用单独的选项设置 SNI。 curl 将始终从请求 URL 中获取它。但是 curl 有一个特殊的--resolve
选项:
为特定主机和端口对提供自定义地址。使用此功能,可以使 curl 请求使用指定的地址,并阻止使用其他正常解析的地址。将其视为命令行上提供的一种/etc/hosts 替代方案。
在这种情况下:
$ curl -I --resolve analytics.usa.gov:443:54.240.184.142 https://analytics.usa.gov
HTTP/1.1 200 OK
Content-Type: text/html
[...]
(443 是 HTTPS 的标准 TCP 端口(
如果要在较低级别进行试验,可以使用openssl
工具与正确的 SNI 建立原始 TLS 连接:
$ openssl s_client -connect 54.240.184.142:443 -servername analytics.usa.gov -crlf
然后,您将能够键入 HTTP 请求并看到正确的响应:
HEAD / HTTP/1.1
Host: analytics.usa.gov
HTTP/1.1 200 OK
Content-Type: text/html
[...]
最后,请注意,在HTTP/2中,有一个名为:authority
的特殊标头(是的,带有冒号(,某些客户端可能会使用它而不是Host
。它们之间的区别在于与 HTTP/1.1 和代理的向后兼容性:有关详细信息,请参阅 RFC 7540 § 8.1.2.3 和 RFC 7230 § 5.3。