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
Go库中的代码不支持此功能。
为什么你至少不想返回一个 400 HTTP响应?
更多关于Golang中Http/server如何设置非TLS请求不响应的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

“我认为这是一个品味问题” 
感谢您的回复。 这是一个Go网络服务器的指纹。
其他网站,如 google.com、cisco.com、apple.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.Config的GetConfigForClient回调函数来实现。当客户端未使用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连接会被立即关闭而不发送任何响应。

