Stripping SNI information from TLS WebSocket connections

六月ゝ 毕业季﹏ 提交于 2019-12-11 13:07:23

问题


I find myself needing to set up a WebSocket connection in a hostile environment in which a firewall sniffs SNI information from TLS which I'd rather it didn't. In my particular case, the WebSocket server does not use SNI for request handling, so as such, the SNI part of the handshake could be safely removed.

My question then becomes: In the golang.org WebSocket package, golang.org/x/net/websocket, what is the simplest way to strip SNI information while retaining validation of the provided chain?

The best I have been able to come up with is to simply replace the hostname of the URL to be dialled with its corresponding IP. This causes crypto/tls to never add the problematic SNI information, but, in the solution I was able to come up with, a custom validator ends up having to be provided to validate the chain:

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
    }
}

This totally works but seems rather clunky. Is there a simpler approach? (Ideally one that is not specific to WebSocket applications but works for TLS in general; the exact same idea as above could be applied to HTTPS.)

来源:https://stackoverflow.com/questions/51250164/stripping-sni-information-from-tls-websocket-connections

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!