curl:如何为 https 请求指定目标主机名

curl: how to specify target hostname for https request

我有一个 x.examplea.exampleb.example 提供流量。 x.example 拥有 a.exampleb.example 的证书。 a.exampleb.example 的 DNS 尚未设置。

如果我为 a.example 添加一个 /etc/hosts 条目指向 x.example 的 ip 和 运行 curl -XGET https://a.example,我得到一个 200。

然而,如果我 运行 curl --header 'Host: a.example' https://x.example,我得到:

curl: (51) SSL: no alternative certificate subject name matches target host name x.example

我认为它会使用 a.example 作为主机。也许我不明白 SNI/TLS 是如何工作的。

因为 a.example 是 HTTP header TLS 握手还不能访问它?但是它确实可以访问 URL 本身?

事实上,TLS 中的 SNI 并不是那样工作的。 SNI,因为与 TLS 相关的一切,发生在任何类型的 HTTP 流量之前,因此在该步骤中不考虑 Host header(但稍后将有助于网络服务器知道哪个主机你也在连接)。

因此,要启用 SNI,您需要在 HTTP 客户端中使用一个特定的开关来告诉它在握手期间使用您需要的主机名值发送适当的 TLS 扩展。

curl 的情况下,您至少需要 7.18.1 版本(基于 https://curl.haxx.se/changes.html)然后它似乎会自动使用 Host header。它也取决于它链接到的 OpenSSL(或您平台上的等效库)版本。

请参阅 https://curl.haxx.se/docs/knownbugs.html 的第 1.10 点,其中提到了一个错误但解释了发生的情况:

When given a URL with a trailing dot for the host name part: "https://example.com./", libcurl will strip off the dot and use the name without a dot internally and send it dot-less in HTTP Host: headers and in the TLS SNI field.

--connect-to 选项对您的情况也很有用。或者 --resolve 代替 /etc/hosts,参见 https://curl.haxx.se/mail/archive-2015-01/0042.html for am example, or https://makandracards.com/makandra/1613-make-an-http-request-to-a-machine-but-fake-the-hostname 您可以在所有情况下添加 --verbose 以更详细地查看正在发生的事情。看这个例子:https://www.claudiokuenzler.com/blog/693/curious-case-of-curl-ssl-tls-sni-http-host-header;您还将在那里看到如何直接使用 openssl.

进行测试

如果你的 /etc/hosts 中有 a.example 你应该 运行 curl with https://a.example/ 它应该照顾 Host header 和 SNI(或使用 --resolve 代替)

所以直接回答你的问题,替换

curl --header 'Host: a.example' https://x.example

curl --connect-to a.example:443:x.example:443 https://a.example

它应该可以完美运行。

所选答案帮助我找到了答案,即使它不包含答案。 mail/archive link Patrick Mevzek 提供的答案有 错误的 端口号。因此,即使遵循该答案也会导致它继续失败。

我使用 this container 到 运行 调试服务器来检查请求。我强烈建议任何调试此类问题的人都这样做。

这是解决 OP 问题的方法。

# Instead of this:
# curl --header 'Host: a.example'        https://x.example

# Do:
  host=a.example
  target=x.example

  ip=$(dig +short $target | head -n1)
  curl -sv   --resolve $host:443:$ip https://$host

如果您想忽略错误的证书匹配,请使用 -svk 而不是 -sv

curl -svk --resolve $host:443:$ip https://$host

注意:由于您使用的是 https,因此必须在 --resolve 参数中使用 443 而不是上所述的 80 mail/archive