curl:如何为 https 请求指定目标主机名
curl: how to specify target hostname for https request
我有一个 x.example
为 a.example
和 b.example
提供流量。
x.example
拥有 a.example
和 b.example
的证书。 a.example
和 b.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
我有一个 x.example
为 a.example
和 b.example
提供流量。
x.example
拥有 a.example
和 b.example
的证书。 a.example
和 b.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