Golang中crypto/tls的MinVersion被忽略问题

Golang中crypto/tls的MinVersion被忽略问题 我正在尝试调试与特定主机的连接失败问题。我记录了通过浏览器向该主机发出的成功请求的数据包捕获,以及通过以下Go代码发出的失败请求:

package main

import (
        "crypto/tls"
        "log"
)

func main() {
        log.SetFlags(log.Lshortfile)

        conf := &tls.Config{
                //InsecureSkipVerify: true,
                MinVersion: tls.VersionTLS13,
                MaxVersion: tls.VersionTLS13,
        }


        conn, err := tls.Dial("tcp", "x.x.x.x:443", conf)
        if err != nil {
                log.Println(err)
                return
        }
        defer conn.Close()

        n, err := conn.Write([]byte("hello\n"))
        if err != nil {
                log.Println(n, err)
                return
        }

        buf := make([]byte, 100)
        n, err = conn.Read(buf)
        if err != nil {
                log.Println(n, err)
                return
        }

        println(string(buf[:n]))
}

在检查了成功和失败请求中的Client Hello数据包后,我注意到成功的请求使用的是TLSv1.3,而失败的请求使用的是TLSv1.2。

我上面的代码指定了在请求中只使用TLSv1.3,但不知何故,请求仍然尝试使用TLSv1.2。我已经尝试查阅了各种用Go编写的示例HTTP客户端,并确认我使用了正确的语法。有人知道我在这里做错了什么吗?


更多关于Golang中crypto/tls的MinVersion被忽略问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中crypto/tls的MinVersion被忽略问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


问题在于tls.ConfigMinVersionMaxVersion设置被tls.Dial函数内部逻辑覆盖。当目标服务器不支持TLS 1.3时,Go的TLS实现会回退到TLS 1.2,即使你明确设置了版本限制。

以下是验证和解决这个问题的示例代码:

package main

import (
    "crypto/tls"
    "log"
    "fmt"
)

func main() {
    log.SetFlags(log.Lshortfile)

    conf := &tls.Config{
        MinVersion: tls.VersionTLS13,
        MaxVersion: tls.VersionTLS13,
    }

    // 添加调试信息
    fmt.Printf("配置的MinVersion: %x\n", conf.MinVersion)
    fmt.Printf("配置的MaxVersion: %x\n", conf.MaxVersion)
    fmt.Printf("TLS 1.3常量值: %x\n", tls.VersionTLS13)

    conn, err := tls.Dial("tcp", "x.x.x.x:443", conf)
    if err != nil {
        log.Printf("连接错误: %v\n", err)
        
        // 尝试获取更详细的错误信息
        if e, ok := err.(*tls.RecordHeaderError); ok {
            log.Printf("TLS记录头错误: %v\n", e)
        }
        return
    }
    defer conn.Close()

    // 验证实际协商的版本
    state := conn.ConnectionState()
    fmt.Printf("协商的TLS版本: %x\n", state.Version)
    fmt.Printf("TLS 1.3协商结果: %v\n", state.Version == tls.VersionTLS13)

    n, err := conn.Write([]byte("hello\n"))
    if err != nil {
        log.Println(n, err)
        return
    }

    buf := make([]byte, 100)
    n, err = conn.Read(buf)
    if err != nil {
        log.Println(n, err)
        return
    }

    println(string(buf[:n]))
}

如果服务器不支持TLS 1.3,连接会失败。要强制只使用TLS 1.3并防止回退,可以使用更严格的配置:

package main

import (
    "crypto/tls"
    "log"
)

func main() {
    log.SetFlags(log.Lshortfile)

    conf := &tls.Config{
        MinVersion: tls.VersionTLS13,
        MaxVersion: tls.VersionTLS13,
        CipherSuites: []uint16{
            tls.TLS_AES_128_GCM_SHA256,
            tls.TLS_AES_256_GCM_SHA384,
            tls.TLS_CHACHA20_POLY1305_SHA256,
        },
    }

    conn, err := tls.Dial("tcp", "x.x.x.x:443", conf)
    if err != nil {
        log.Println(err)
        return
    }
    defer conn.Close()

    n, err := conn.Write([]byte("hello\n"))
    if err != nil {
        log.Println(n, err)
        return
    }

    buf := make([]byte, 100)
    n, err = conn.Read(buf)
    if err != nil {
        log.Println(n, err)
        return
    }

    println(string(buf[:n]))
}

要诊断服务器支持的TLS版本,可以使用这个测试代码:

package main

import (
    "crypto/tls"
    "fmt"
    "log"
)

func testTLSVersion(version uint16, versionName string) {
    conf := &tls.Config{
        MinVersion: version,
        MaxVersion: version,
        InsecureSkipVerify: true,
    }

    conn, err := tls.Dial("tcp", "x.x.x.x:443", conf)
    if err != nil {
        fmt.Printf("%s 连接失败: %v\n", versionName, err)
        return
    }
    defer conn.Close()
    
    state := conn.ConnectionState()
    fmt.Printf("%s 连接成功,协商版本: %x\n", versionName, state.Version)
}

func main() {
    log.SetFlags(log.Lshortfile)

    fmt.Println("测试服务器支持的TLS版本:")
    testTLSVersion(tls.VersionTLS13, "TLS 1.3")
    testTLSVersion(tls.VersionTLS12, "TLS 1.2")
    testTLSVersion(tls.VersionTLS11, "TLS 1.1")
    testTLSVersion(tls.VersionTLS10, "TLS 1.0")
}

运行这个测试可以确定服务器实际支持的TLS版本。如果服务器不支持TLS 1.3,你的原始代码会回退到TLS 1.2,这是Go TLS实现的默认行为。

回到顶部