如何使用无效证书进行https请求?

huangapple go评论94阅读模式
英文:

How to do a https request with bad certificate?

问题

假设我想以编程方式获取 https://golang.org。目前 golang.org(ssl)有一个错误的证书,该证书是颁发给 *.appspot.com 的。所以当我运行以下代码时:

package main

import (
	"log"
	"net/http"
)

func main() {
	_, err := http.Get("https://golang.org/")
	if err != nil {
		log.Fatal(err)
	}
}

我得到了以下结果(正如我所预期的):

Get https://golang.org/: certificate is valid for *.appspot.com, *.*.appspot.com, appspot.com, not golang.org

现在,我想自己信任这个证书(想象一个我可以验证指纹等的自签名证书):我该如何发出请求并验证/信任该证书?

我可能需要使用openssl下载证书,将其加载到我的文件中,并填充 tls.Config 结构体!?

英文:

Say I want to get https://golang.org programatically. Currently golang.org (ssl) has a bad certificate which is issued to *.appspot.com So when I run this:

package main

import (
	"log"
	"net/http"
)

func main() {
	_, err := http.Get("https://golang.org/")
	if err != nil {
		log.Fatal(err)
	}
}

I get (as I expected)

Get https://golang.org/: certificate is valid for *.appspot.com, *.*.appspot.com, appspot.com, not golang.org

Now, I want to trust this certificate myself (imagine a self-issued certificate where I can validate fingerprint etc.): how can I make a request and validate/trust the certificate?

I probably need to use openssl to download the certificate, load it into my file and fill tls.Config struct !?

答案1

得分: 415

安全提示:禁用安全检查是危险的,应该避免使用

您可以全局禁用默认客户端的所有请求的安全检查:

package main

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

func main() {
    http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	_, err := http.Get("https://golang.org/")
	if err != nil {
		fmt.Println(err)
	}
}

您可以为一个客户端禁用安全检查:

package main

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

func main() {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	_, err := client.Get("https://golang.org/")
	if err != nil {
		fmt.Println(err)
	}
}
英文:

Security note: Disabling security checks is dangerous and should be avoided

You can disable security checks globally for all requests of the default client:

package main

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

func main() {
    http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	_, err := http.Get("https://golang.org/")
	if err != nil {
		fmt.Println(err)
	}
}

You can disable security check for a client:

package main

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

func main() {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	_, err := client.Get("https://golang.org/")
	if err != nil {
		fmt.Println(err)
	}
}

答案2

得分: 49

Proper way (as of Go 1.13) (provided by answer below):

customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
client := &http.Client{Transport: customTransport}

Original Answer:

Here's a way to do it without losing the default settings of the DefaultTransport, and without needing the fake request as per user comment.

defaultTransport := http.DefaultTransport.(*http.Transport)

// Create new Transport that ignores self-signed SSL
customTransport := &http.Transport{
  Proxy:                 defaultTransport.Proxy,
  DialContext:           defaultTransport.DialContext,
  MaxIdleConns:          defaultTransport.MaxIdleConns,
  IdleConnTimeout:       defaultTransport.IdleConnTimeout,
  ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout,
  TLSHandshakeTimeout:   defaultTransport.TLSHandshakeTimeout,
  TLSClientConfig:       &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: customTransport}

Shorter way:

customTransport := &(*http.DefaultTransport.(*http.Transport)) // make shallow copy
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
client := &http.Client{Transport: customTransport}

Warning: For testing/development purposes only. Anything else, proceed at your own risk!!!

英文:

Proper way (as of Go 1.13) (provided by answer below):

customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
client := &http.Client{Transport: customTransport}

Original Answer:

Here's a way to do it without losing the default settings of the DefaultTransport, and without needing the fake request as per user comment.

defaultTransport := http.DefaultTransport.(*http.Transport)

// Create new Transport that ignores self-signed SSL
customTransport := &http.Transport{
  Proxy:                 defaultTransport.Proxy,
  DialContext:           defaultTransport.DialContext,
  MaxIdleConns:          defaultTransport.MaxIdleConns,
  IdleConnTimeout:       defaultTransport.IdleConnTimeout,
  ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout,
  TLSHandshakeTimeout:   defaultTransport.TLSHandshakeTimeout,
  TLSClientConfig:       &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: customTransport}

Shorter way:

customTransport := &(*http.DefaultTransport.(*http.Transport)) // make shallow copy
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
client := &http.Client{Transport: customTransport}

Warning: For testing/development purposes only. Anything else, proceed at your own risk!!!

答案3

得分: 30

所有这些答案都是错误的!不要使用InsecureSkipVerify来处理与主机名不匹配的CN。Go开发人员不明智地坚持不禁用主机名检查(这是合法的用途-隧道、nats、共享集群证书等),同时还有一个看起来相似但实际上完全忽略证书检查的东西。您需要知道证书是有效的并由您信任的证书签名的。但在常见情况下,您知道CN与您连接的主机名不匹配。对于这些情况,在tls.Config上设置ServerName。如果tls.Config.ServerName == remoteServerCN,则证书检查将成功。这就是您想要的。InsecureSkipVerify意味着没有身份验证;它很容易受到中间人攻击;这违背了使用TLS的目的。

InsecureSkipVerify有一个合法的用途:使用它连接到主机并获取其证书,然后立即断开连接。如果您设置代码使用InsecureSkipVerify,通常是因为您没有正确设置ServerName(它需要来自环境变量或其他地方-不要对此要求抱怨...正确地做)。

特别是,如果您使用客户端证书并依赖它们进行身份验证,那么您基本上有一个不再实际登录的虚假登录。拒绝使用InsecureSkipVerify的代码,否则您将以艰难的方式了解其中的问题!

英文:

All of these answers are wrong! Do not use InsecureSkipVerify to deal with a CN that doesn't match the hostname. The Go developers unwisely were adamant about not disabling hostname checks (which has legitimate uses - tunnels, nats, shared cluster certs, etc), while also having something that looks similar but actually completely ignores the certificate check. You need to know that the certificate is valid and signed by a cert that you trust. But in common scenarios, you know that the CN won't match the hostname you connected with. For those, set ServerName on tls.Config. If tls.Config.ServerName == remoteServerCN, then the certificate check will succeed. This is what you want. InsecureSkipVerify means that there is NO authentication; and it's ripe for a Man-In-The-Middle; defeating the purpose of using TLS.

There is one legitimate use for InsecureSkipVerify: use it to connect to a host and grab its certificate, then immediately disconnect. If you setup your code to use InsecureSkipVerify, it's generally because you didn't set ServerName properly (it will need to come from an env var or something - don't belly-ache about this requirement... do it correctly).

In particular, if you use client certs and rely on them for authentication, you basically have a fake login that doesn't actually login any more. Refuse code that does InsecureSkipVerify, or you will learn what is wrong with it the hard way!

答案4

得分: 15

正确的方法是,如果你想保持默认的传输设置,现在(从Go 1.13开始)应该这样做:

customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
client = &http.Client{Transport: customTransport}

Transport.Clone会对传输进行深拷贝。这样你就不必担心随着时间推移,Transport结构体中添加的任何新字段会被遗漏。

英文:

The correct way to do this if you want to maintain the default transport settings is now (as of Go 1.13):

customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
client = &http.Client{Transport: customTransport}

Transport.Clone makes a deep copy of the transport. This way you don't have to worry about missing any new fields that get added to the Transport struct over time.

答案5

得分: 6

如果您想使用http包的默认设置,那么您不需要创建新的Transport和Client对象,您可以像这样更改以忽略证书验证:

tr := http.DefaultTransport.(*http.Transport)
tr.TLSClientConfig.InsecureSkipVerify = true
英文:

If you want to use the default settings from http package, so you don't need to create a new Transport and Client object, you can change to ignore the certificate verification like this:

tr := http.DefaultTransport.(*http.Transport)
tr.TLSClientConfig.InsecureSkipVerify = true

答案6

得分: 1

通常情况下,URL的DNS域名必须与证书的主题相匹配。

在过去,可以通过将域名设置为证书的cn或将域名设置为主题备用名称来实现。

对于cn的支持已经被弃用了很长时间(自2000年在RFC 2818中),Chrome浏览器甚至不再查看cn,所以今天你需要将URL的DNS域名作为主题备用名称。

RFC 6125禁止检查cn是否存在DNS域名的SAN,但如果存在IP地址的SAN,则不禁止检查。RFC 6125还重申了cn已经被弃用,这在RFC 2818中已经提到过。而证书颁发机构浏览器论坛要求存在,这与RFC 6125的结合实际上意味着cn将永远不会被检查DNS域名。

英文:

Generally, The DNS Domain of the URL MUST match the Certificate Subject of the certificate.

In former times this could be either by setting the domain as cn of the certificate or by having the domain set as a Subject Alternative Name.

Support for cn was deprecated for a long time (since 2000 in RFC 2818) and Chrome browser will not even look at the cn anymore so today you need to have the DNS Domain of the URL as a Subject Alternative Name.

RFC 6125 which forbids checking the cn if SAN for DNS Domain is present, but not if SAN for IP Address is present. RFC 6125 also repeats that cn is deprecated which was already said in RFC 2818. And the Certification Authority Browser Forum to be present which in combination with RFC 6125 essentially means that cn will never be checked for DNS Domain name.

huangapple
  • 本文由 发表于 2012年8月25日 20:57:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/12122159.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定