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
更多关于Golang中crypto/tls的MinVersion被忽略问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
问题在于tls.Config的MinVersion和MaxVersion设置被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实现的默认行为。

