我发现自己需要在一个敌对的环境中建立WebSocket连接,在该环境中防火墙希望从TLS嗅探TLS中的SNI信息。在我的特定情况下,WebSocket服务器不使用SNI进行请求处理,因此,可以安全地删除握手的SNI部分。
然后我的问题变成了:在golang.org WebSocket软件包golang.org/x/net/websocket
中,最简单的剥离SNI信息同时保留对所提供链的验证的方法是什么?
我能想到的最好的办法就是简单地用相应的IP替换要拨打的URL的主机名。这导致crypto/tls
永远不会添加有问题的SNI信息,但是,在我能够提出的解决方案中,最终不得不提供一个自定义验证器来验证链:
func dial(url string, origin string) (*websocket.Conn, error) {
// Use system resolver to get IP of host
hostRegExp := regexp.MustCompile("//([^/]+)/")
host := hostRegExp.FindStringSubmatch(url)[1]
addrs, err := net.LookupHost(host)
if err != nil {
return fmt.Errorf("Could not resolve address of %s: %v", host, err)
}
ip := addrs[0]
// Replace the hostname in the given URL with its IP instead
newURL := strings.Replace(url, host, ip, 1)
config, _ := websocket.NewConfig(newURL, origin)
// As we have removed the hostname, the Go TLS package will not know what to
// validate the certificate DNS names against, so we have to provide a custom
// verifier based on the hostname we threw away.
config.TlsConfig = &tls.Config{
InsecureSkipVerify: true,
VerifyPeerCertificate: verifier(host),
}
return websocket.DialConfig(config)
}
func verifier(host string) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
// For simplicity, let us only consider the case in which the first certificate is the one
// to validate, and in which it is signed directly by a CA, with no parsing of
// intermediate certificates required.
opts := x509.VerifyOptions{
DNSName: host,
}
rawCert := rawCerts[0]
cert, err := x509.ParseCertificate(rawCert)
if err != nil {
return err
}
_, err = cert.Verify(opts)
return err
}
}
这完全有效,但似乎很笨拙。有没有更简单的方法? (理想情况下,这不是特定于WebSocket应用程序,而是通常适用于TLS;与上述完全相同的想法可以应用于HTTPS。)