【问题标题】:Go httputil.ReverseProxy not overriding the Host header去 httputil.ReverseProxy 不覆盖主机头
【发布时间】:2019-03-30 09:21:49
【问题描述】:

我基本上是在尝试编写一个反向代理服务器,以便当我 curl localhost:8080/get 时,它将请求代理到 https://nghttp2.org/httpbin/get

注意:上面列出的https://nghttp2.org/httpbin/get 服务是http/2。但是这种行为也会发生在 http/1 上,例如 https://httpbin.org/get

我为此使用httputil.ReverseProxy,并且我正在重写URL,同时自定义Host 标头以不将localhost:8080 泄漏到实际后端。

但是,无论我在标头上设置多少次,请求仍然会以Host: localhost:8080 到达后端。同样,我使用mitmproxy 窥探请求,看起来net/http.Client 将:authority 伪标头设置为localhost:8080

这是我的源代码:

package main

import (
    "log"
    "net/http"
    "net/http/httputil"
)

func main() {
    proxy := &httputil.ReverseProxy{
        Transport: roundTripper(rt),
        Director: func(req *http.Request) {
            req.URL.Scheme = "https"
            req.URL.Host = "nghttp2.org"
            req.URL.Path = "/httpbin" + req.URL.Path
            req.Header.Set("Host", "nghttp2.org") // <--- I set it here first
        },
    }
    log.Fatal(http.ListenAndServe(":8080", proxy))
}

func rt(req *http.Request) (*http.Response, error) {
    log.Printf("request received. url=%s", req.URL)
    req.Header.Set("Host", "nghttp2.org") // <--- I set it here as well
    defer log.Printf("request complete. url=%s", req.URL)

    return http.DefaultTransport.RoundTrip(req)
}


// roundTripper makes func signature a http.RoundTripper
type roundTripper func(*http.Request) (*http.Response, error)
func (f roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) }

当我查询curl localhost:8080/get 时,请求被代理到https://nghttp2.org/httpbin/get。回显的响应清楚地表明,我设置 Host 标头的指令没有做任何事情:

{
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip",
    "Host": "localhost:8080",
    "User-Agent": "curl/7.54.0"
  },
  "origin": "2601:602:9c02:16c2:fca3:aaab:3914:4a71",
  "url": "https://localhost:8080/httpbin/get"
}

mitmproxy snooping 还清楚地表明该请求是在 :authority 伪标头设置为 localhost:8080 的情况下发出的:

【问题讨论】:

    标签: go


    【解决方案1】:

    来自http.Requestdocs

            // For server requests, Host specifies the host on which the URL
            // is sought. Per RFC 7230, section 5.4, this is either the value
            // of the "Host" header or the host name given in the URL itself.
            // It may be of the form "host:port". For international domain
            // names, Host may be in Punycode or Unicode form. Use
            // golang.org/x/net/idna to convert it to either format if
            // needed.
            // To prevent DNS rebinding attacks, server Handlers should
            // validate that the Host header has a value for which the
            // Handler considers itself authoritative. The included
            // ServeMux supports patterns registered to particular host
            // names and thus protects its registered Handlers.
            //
            // For client requests, Host optionally overrides the Host
            // header to send. If empty, the Request.Write method uses
            // the value of URL.Host. Host may contain an international
            // domain name.
            Host string
    

    所以URL.Host 的值仅在request.Host 为空的情况下使用,情况并非如此。设置request.Host 应该可以解决问题:

    req.Host = "nghttp2.org"
    

    相关问题已讨论here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-27
      相关资源
      最近更新 更多