Golang中如何获取TLS请求的域名

Golang中如何获取TLS请求的域名

func main() {
    fmt.Println("hello world")
}

在使用自签名证书的TLS Go服务器监听器时,如何获取域名?如果浏览器显示证书通知且未添加例外,只有一条请求会发送到服务器。域名信息存在于net.Conn中,虽然可以通过fmt.Println(conn)查看,但如何将该名称以字符串形式存入变量?

6 回复

基本思路是在证书不匹配时检测请求的域名。

更多关于Golang中如何获取TLS请求的域名的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


首先,在生成自签名证书时,请务必注意完全限定域名(FQDN)并正确填写域名。其次,不要使用Chrome处理自签名证书。Chrome进行了一些更改,处理此类证书较为困难。建议改用Firefox。如果握手成功,您可以进行日志记录或其他操作。

您可以挂接到 TLS 配置的 GetCertificate 方法:

tlsCfg := &tls.Config{
		GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
			fmt.Println("client requested", hello.ServerName)
			return nil, errors.New("no certificate for you")
		},
 ...

在您拥有与请求名称匹配的证书的情况下,您还需要实际提供该证书。如果只有一个证书,您可以始终提供该证书。

问题不在于创建TLS服务器,而在于当服务器运行时获取请求的域名。如果使用自签名证书,用户无法接受,这种情况下只会有一个请求到达服务器。我想知道请求使用的是哪个域名。

示例:

  1. 客户端在浏览器中打开地址 https://somestrange.com
  2. 服务器接收请求并返回证书(握手)
  3. 在日志文件中写入请求的域名 somestrange.com
  4. 客户端未将证书添加到例外并关闭浏览器

我需要实现第3步。

这是"网络TLS连接自签名证书"在谷歌上的第一个搜索结果,其中包含一些可能对你有用的示例。

gist.github.com

https://gist.github.com/denji/12b3a568f092ab951456

golang-tls.md
### 已移至Git仓库:https://github.com/denji/golang-tls

##### 生成私钥(.key)

```sh
# RSA算法密钥考虑因素 ≥ 2048位
openssl genrsa -out server.key 2048

# ECDSA算法密钥考虑因素 ≥ secp384r1
# 列出支持的ECDSA曲线(openssl ecparam -list_curves)

该文件已被截断。显示原文

你需要将自签名证书添加到连接来源计算机的证书存储中。

在Go语言中,可以通过类型断言从net.Conn中获取TLS连接信息,然后访问ConnectionState来提取客户端请求的域名。以下是具体实现方法:

package main

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

func main() {
    // 创建自签名证书的TLS配置
    cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
    if err != nil {
        log.Fatal(err)
    }
    
    config := &tls.Config{
        Certificates: []tls.Certificate{cert},
    }

    // 创建TLS监听器
    listener, err := tls.Listen("tcp", ":8443", config)
    if err != nil {
        log.Fatal(err)
    }
    defer listener.Close()

    fmt.Println("TLS服务器监听在 :8443")

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        // 从TLS连接中获取域名
        if tlsConn, ok := r.TLS; ok {
            serverName := tlsConn.ServerName
            fmt.Fprintf(w, "请求的域名: %s\n", serverName)
            
            // 将域名存入变量
            domain := serverName
            fmt.Printf("获取到的域名已存入变量: %s\n", domain)
        } else {
            fmt.Fprintf(w, "非TLS连接\n")
        }
    })

    log.Fatal(http.Serve(listener, nil))
}

对于原始套接字级别的处理,可以使用以下方法:

package main

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

func handleConnection(conn net.Conn) {
    defer conn.Close()
    
    // 类型断言为TLS连接
    if tlsConn, ok := conn.(*tls.Conn); ok {
        // 完成TLS握手
        err := tlsConn.Handshake()
        if err != nil {
            log.Printf("TLS握手失败: %v", err)
            return
        }
        
        // 获取连接状态
        state := tlsConn.ConnectionState()
        serverName := state.ServerName
        
        // 将域名存入变量
        domain := serverName
        fmt.Printf("客户端请求的域名: %s\n", domain)
        fmt.Printf("域名已存入变量: %s\n", domain)
        
        // 其他处理逻辑...
    } else {
        fmt.Println("非TLS连接")
    }
}

func main() {
    cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
    if err != nil {
        log.Fatal(err)
    }
    
    config := &tls.Config{
        Certificates: []tls.Certificate{cert},
    }

    listener, err := tls.Listen("tcp", ":8443", config)
    if err != nil {
        log.Fatal(err)
    }
    defer listener.Close()

    fmt.Println("TLS服务器监听在 :8443")

    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Printf("接受连接失败: %v", err)
            continue
        }
        go handleConnection(conn)
    }
}

关键点说明:

  • 使用类型断言conn.(*tls.Conn)将普通连接转换为TLS连接
  • 调用Handshake()方法完成TLS握手
  • 通过ConnectionState().ServerName获取客户端请求的域名
  • 在HTTP处理中,可以直接通过r.TLS.ServerName获取域名

这样就能将TLS请求中的域名信息提取并存储到字符串变量中。

回到顶部