英文:
Disable Common Name Validation - Go HTTP Client
问题
你可以通过设置ServerName
字段为空字符串来禁用Go HTTP客户端中的通用名称验证。根据文档所述,ServerName
字段用于验证返回的证书的主机名,除非设置了InsecureSkipVerify
。因此,将ServerName
设置为空字符串将绕过通用名称验证。
以下是示例代码:
import (
"crypto/tls"
"net/http"
)
func main() {
// 创建自定义的TLS配置
tlsConfig := &tls.Config{
ServerName: "", // 将ServerName设置为空字符串
}
// 创建自定义的HTTP客户端
httpClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
}
// 使用自定义的HTTP客户端发送请求
response, err := httpClient.Get("https://example.com")
if err != nil {
// 处理错误
return
}
// 处理响应
defer response.Body.Close()
// ...
}
通过将ServerName
字段设置为空字符串,你将禁用通用名称验证,但仍然进行TLS握手。请注意,这可能会导致安全风险,因为你不再验证服务器的主机名。请确保在使用此配置时谨慎操作。
英文:
How do I disable common name validation inside of a go http client. I am doing mutual TLS with a common CA and hence common name validation means nothing.
The tls docs say,
// ServerName is used to verify the hostname on the returned
// certificates unless InsecureSkipVerify is given. It is also included
// in the client's handshake to support virtual hosting unless it is
// an IP address.
ServerName string
I don't want to do InsecureSkipVerify
but I don't want to validate the common name.
答案1
得分: 3
你需要传递一个tls.Config
结构体,并在其中使用自己的VerifyPeerCertificate
函数,然后你可以自己检查证书。
VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
如果正常的验证失败,那么在考虑此回调函数之前,握手将中止。如果通过设置
InsecureSkipVerify
来禁用正常的验证,那么将考虑此回调函数,但verifiedChains
参数将始终为nil
。
你可以在这里找到一个验证证书的示例。如果你在这里查看,你会发现即使在这个验证过程中也包括检查主机名,但幸运的是,如果它设置为空字符串,它会跳过。
所以,基本上你需要编写自己的VerifyPeerCertificate
函数,将rawCerts [][]byte
转换为类似以下的形式:
customVerify := func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
roots := x509.NewCertPool()
for _, rawCert := range rawCerts {
cert, _ := x509.ParseCertificate(rawCert)
roots.AddCert(cert)
}
opts := x509.VerifyOptions{
Roots: roots,
}
_, err := cert.Verify(opts)
return err
}
conf := tls.Config{
//...
VerifyPeerCertificate: customVerify,
}
英文:
You would pass a tls.Config
struct with your own VerifyPeerCertificate
function, and then you would check the certificate yourself.
VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
> If normal verification fails then the handshake will abort before
considering this callback. If normal verification is disabled by
setting InsecureSkipVerify then this callback will be considered but
the verifiedChains argument will always be nil.
You can look here for an example of how to verify a certificate. Iif you look here, you'll see that part of even this verification process includes checking the hostname, but luckily you'll see that it skips it if it's set to the empty string.
So, basically you write your own VerifyPeerCertificate
function, convert the rawCerts [][]byte
, which I think would look something like:
customVerify := func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
roots := x509.NewCertPool()
for _, rawCert := range rawCerts {
cert, _ := x509.ParseCertificate(rawCert)
roots.AddCert(cert)
}
opts := x509.VerifyOptions{
Roots: roots,
}
_, err := cert.Verify(opts)
return err
}
conf := tls.Config{
//...
VerifyPeerCertificate: customVerify,
}
答案2
得分: 2
正常的https post请求如下所示:
pool := x509.NewCertPool()
caStr, err := ioutil.ReadFile(serverCAFile)
if err != nil {
return nil, fmt.Errorf("读取服务器CA文件失败")
}
pool.AppendCertsFromPEM(caStr)
tr := &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
},
}
client := &http.Client{Transport: tr}
client.Post(url, bodyType, body)
但是,如果你的URL使用IP地址(例如https://127.0.0.1:8080/api/test),或者URL与证书的通用名称不匹配,并且你只想忽略证书的通用名称检查,应该按照以下方式操作:
pool := x509.NewCertPool()
caStr, err := ioutil.ReadFile(serverCAFile)
if err != nil {
return nil, fmt.Errorf("读取服务器CA文件失败")
}
block, _ := pem.Decode(caStr)
if block == nil {
return nil, fmt.Errorf("解码CA文件失败")
}
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
return nil, fmt.Errorf("解码CA块文件失败")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("解析CA块文件失败")
}
pool.AddCert(cert)
tr := &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
ServerName: cert.Subject.CommonName, //手动设置ServerName
},
}
client := &http.Client{Transport: tr}
client.Post(url, bodyType, body)
希望对你有帮助!
英文:
Normal https post like this
pool := x509.NewCertPool()
caStr, err := ioutil.ReadFile(serverCAFile)
if err != nil {
return nil, fmt.Errorf("read server ca file fail")
}
pool.AppendCertsFromPEM(caStr)
tr := &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
},
}
client := &http.Client{Transport: tr}
client.Post(url, bodyType, body)
But if your url is use ip(ex. https://127.0.0.1:8080/api/test) or you URL is not match certificate common name, and you want to only ignore certificate common name check, should do like this
pool := x509.NewCertPool()
caStr, err := ioutil.ReadFile(serverCAFile)
if err != nil {
return nil, fmt.Errorf("read server ca file fail")
}
block, _ := pem.Decode(caStr)
if block == nil {
return nil, fmt.Errorf("Decode ca file fail")
}
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
return nil, fmt.Errorf("Decode ca block file fail")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("ParseCertificate ca block file fail")
}
pool.AddCert(cert)
tr := &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
ServerName: cert.Subject.CommonName, //manual set ServerName
},
}
client := &http.Client{Transport: tr}
client.Post(url, bodyType, body)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论