Golang中Http/server如何设置非TLS请求不响应

Golang中Http/server如何设置非TLS请求不响应 大家好,

我正在寻找一种方法,使得当调用方未使用 TLS 时,TLS 服务器不作出响应。

			c.rwc.SetReadDeadline(time.Now().Add(d))
		}
		if d := c.server.WriteTimeout; d > 0 {
			c.rwc.SetWriteDeadline(time.Now().Add(d))
		}
		if err := tlsConn.HandshakeContext(ctx); err != nil {
			// If the handshake failed due to the client not speaking
			// TLS, assume they're speaking plaintext HTTP and write a
			// 400 response on the TLS conn's underlying net.Conn.
			if re, ok := err.(tls.RecordHeaderError); ok && re.Conn != nil && tlsRecordHeaderLooksLikeHTTP(re.RecordHeader) {
				io.WriteString(re.Conn, "HTTP/1.0 400 Bad Request\r\n\r\nClient sent an HTTP request to an HTTPS server.\n")
				re.Conn.Close()
				return
			}
			c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)
			return
		}
		c.tlsState = new(tls.ConnectionState)
		*c.tlsState = tlsConn.ConnectionState()
		if proto := c.tlsState.NegotiatedProtocol; validNextProto(proto) {
			if fn := c.server.TLSNextProto[proto]; fn != nil {

例如,当你尝试访问 http://google.com:443 时,它将不会响应。

你可以用以下代码进行测试:

package main
  
import (
        "net/http"
        "log"
)

func main() {
        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
                w.Write([]byte("test"))
        })
        err := http.ListenAndServeTLS(":443", "cert.pem", "key.key", nil)
        if err != nil {
                log.Println(err)
        }
}

然后在浏览器中访问 http://localhost:443

此致


更多关于Golang中Http/server如何设置非TLS请求不响应的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

Go库中的代码不支持此功能。 为什么你至少不想返回一个 400 HTTP响应?

更多关于Golang中Http/server如何设置非TLS请求不响应的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


“我认为这是一个品味问题” 👍

感谢您的回复。 这是一个Go网络服务器的指纹。

其他网站,如 google.comcisco.comapple.com,不会响应使用HTTPS端口(例如 http://google.com:443)的HTTP请求。

其他一些服务则会重定向到HTTPS,例如 golang.org

如果可能的话,我希望在Go中控制这种行为。

你的观察并不完全正确。比较以下两种情况:

❯ curl -v http://golang.org
* Rebuilt URL to: http://golang.org/
*   Trying 172.217.23.17...
* Connected to golang.org (172.217.23.17) port 80 (#0)
> GET / HTTP/1.1
> Host: golang.org
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 302 Found
< Date: Thu, 06 May 2021 12:45:08 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 42
< Location: https://golang.org/
< Via: 1.1 google
<
<a href="https://golang.org/">Found</a>.

这是预期的情况:一个HTTP请求被重定向到HTTPS请求。但是:

❯ curl -v http://golang.org:443
* Rebuilt URL to: http://golang.org:443/
*   Trying 172.217.23.17...
* Connected to golang.org (172.217.23.17) port 443 (#0)
> GET / HTTP/1.1
> Host: golang.org:443
> User-Agent: curl/7.43.0
> Accept: */*
>
* Recv failure: Connection reset by peer
* Closing connection 0

一个针对HTTPS端口443的HTTP请求被拒绝了。

我认为,你是更喜欢这种“连接被对端重置”的行为,还是Go标准库实现的400 Bad Request响应,这取决于个人偏好。

无论如何,如果你想对HTTP请求的处理方式有更多控制,可以在你的Go代码前面放置一个像NGINX这样的HTTP反向代理。

要实现TLS服务器不响应非TLS请求,可以通过自定义tls.ConfigGetConfigForClient回调函数来实现。当客户端未使用TLS时,可以返回一个空的TLS配置,使连接立即失败。

以下是示例代码:

package main

import (
    "crypto/tls"
    "net/http"
    "log"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("test"))
    })

    server := &http.Server{
        Addr: ":443",
        TLSConfig: &tls.Config{
            GetConfigForClient: func(hello *tls.ClientHelloInfo) (*tls.Config, error) {
                // 如果客户端未发送TLS ClientHello,直接返回空配置使连接失败
                if hello == nil || len(hello.SupportedVersions) == 0 {
                    return &tls.Config{}, nil
                }
                // 正常TLS连接返回默认配置
                return &tls.Config{
                    Certificates: []tls.Certificate{loadCert()},
                }, nil
            },
        },
    }

    log.Fatal(server.ListenAndServeTLS("", ""))
}

func loadCert() tls.Certificate {
    cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
    if err != nil {
        log.Fatal(err)
    }
    return cert
}

另一种更直接的方法是在TLS握手前检查连接的第一个字节,如果不是TLS记录头则立即关闭连接:

package main

import (
    "bufio"
    "crypto/tls"
    "net"
    "net/http"
    "log"
)

type tlsOnlyListener struct {
    net.Listener
}

func (l *tlsOnlyListener) Accept() (net.Conn, error) {
    conn, err := l.Listener.Accept()
    if err != nil {
        return nil, err
    }

    // 窥探第一个字节来判断是否为TLS连接
    bufConn := bufio.NewReader(conn)
    firstByte, err := bufConn.Peek(1)
    if err != nil {
        conn.Close()
        return nil, err
    }

    // TLS记录的第一个字节应该是0x16 (TLS握手)
    if firstByte[0] != 0x16 {
        conn.Close()
        return nil, nil // 静默丢弃非TLS连接
    }

    return &peekedConn{conn, bufConn}, nil
}

type peekedConn struct {
    net.Conn
    buf *bufio.Reader
}

func (c *peekedConn) Read(b []byte) (int, error) {
    return c.buf.Read(b)
}

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("test"))
    })

    listener, err := net.Listen("tcp", ":443")
    if err != nil {
        log.Fatal(err)
    }

    tlsListener := &tlsOnlyListener{listener}
    tlsConfig := &tls.Config{
        Certificates: []tls.Certificate{loadCert()},
    }

    server := &http.Server{
        TLSConfig: tlsConfig,
    }

    log.Fatal(server.ServeTLS(tlsListener, "", ""))
}

func loadCert() tls.Certificate {
    cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
    if err != nil {
        log.Fatal(err)
    }
    return cert
}

这两种方法都能确保只有TLS连接会被处理,非TLS连接会被立即关闭而不发送任何响应。

回到顶部