【问题标题】:curl: how to specify target hostname for https requestcurl:如何为 https 请求指定目标主机名
【发布时间】:2018-10-21 02:25:10
【问题描述】:

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

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

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

curl: (51) SSL: 没有替代证书主题名称与目标匹配 主机名 x.example

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

因为a.example 是 HTTP 标头,TLS 握手还没有访问它的权限?但它确实有权访问的 URL 本身?

【问题讨论】:

    标签: ssl curl sni


    【解决方案1】:

    确实,TLS 中的 SNI 不能这样工作。 SNI,与 TLS 相关的所有内容,都发生在任何类型的 HTTP 流量之前,因此在该步骤中不考虑 Host 标头(但稍后将有助于网络服务器知道您正在连接的主机)。

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

    对于curl,您至少需要7.18.1 版本(基于https://curl.haxx.se/changes.html),然后它似乎会自动使用Host 标头中提供的值。它还取决于它链接到的 OpenSSL(或您平台上的等效库)版本。

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

    当给定一个带有尾随点作为主机名部分的 URL:“https://example.com./”时,libcurl 将去掉点并在内部使用不带点的名称,并将其发送到无点HTTP 主机:标头和 TLS SNI 字段中。

    --connect-to 选项在您的情况下也很有用。或--resolve 替代/etc/hosts,例如见https://curl.haxx.se/mail/archive-2015-01/0042.html,或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,你应该只用https://a.example/ 运行curl,它应该处理Host 标头和SNI(或改用--resolve

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

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

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

    它应该可以完美运行。

    【讨论】:

      【解决方案2】:

      选择的答案帮助我找到了答案,即使它不包含答案。 Patrick Mevzek 提供的邮件/存档链接中的答案有 wrong 端口号。因此,即使遵循该答案也会导致它继续失败。

      我使用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 中所述

      【讨论】:

      • 在 Mac 上非常有用,因为 Catalina 似乎不再允许您手动配置主机文件。
      • 代替`ip=$(dig +short x.example | head -n1)`使用`ip=$(dig +short "$target" | head -n1)`
      • 噢!已更正。谢谢你,@Rax。 (我想念你的roast beef sandwiches。Arby 的差。)
      • @SamCritchley 我不认为这是正确的。 /etc/hosts 仍然有效,但您需要正确的上下文。它不是应该支持它的操作系统,而是应用程序/浏览器。例如,Safari 发送一个 https 绑定 dns 请求,如果得到答案,它会忽略 /etc/hosts。
      • 是的,你是对的@Lethargos,对此感到抱歉。
      猜你喜欢
      • 2019-04-30
      • 2012-01-21
      • 2014-07-09
      • 1970-01-01
      • 1970-01-01
      • 2022-01-24
      • 2021-07-20
      • 2013-02-05
      • 2021-03-16
      相关资源
      最近更新 更多