Comment faire une requête https avec un mauvais certificat?

128

Disons que je veux être https://golang.orgprogrammé. Actuellement, golang.org (ssl) a un mauvais certificat qui est émis à *.appspot.comSo quand je lance ceci:

package main

import (
    "log"
    "net/http"
)

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

Je reçois (comme je m'y attendais)

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

Maintenant, je veux faire confiance à ce certificat moi-même (imaginez un certificat auto-émis où je peux valider une empreinte digitale, etc.): comment puis-je faire une demande et valider / faire confiance au certificat?

J'ai probablement besoin d'utiliser openssl pour télécharger le certificat, le charger dans mon fichier et remplir tls.Configstruct!?

topskip
la source
5
ce n'est pas un "mauvais certificat", c'est un certificat avec un CN différent. InsecureSkipVerify n'est pas une utilisation légitime ici. Vous devez définir ServerName dans le tls.Config pour correspondre à ce à quoi vous essayez de vous connecter. Cette publication de StackOverflow provoque la propagation de cette grande faille de sécurité dans le code Go partout. InsecureSkipVerify ne vérifie pas du tout le certificat. Ce que vous voulez, c'est vérifier que le certificat a été légitimement signé par une entité de confiance, même si le CN ne correspond pas au nom d'hôte. Les tunnels et NATS peuvent légitimement entraîner une discordance.
Rob

Réponses:

283

Note de sécurité: la désactivation des contrôles de sécurité est dangereuse et doit être évitée

Vous pouvez désactiver globalement les contrôles de sécurité pour toutes les demandes du client par défaut:

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)
    }
}

Vous pouvez désactiver le contrôle de sécurité pour un 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)
    }
}
cyberdelia
la source
17
Je me demande où mettre un certificat de confiance pour que la connexion puisse être utilisée sans InsecureSkipVerify: true. Est-ce possible?
topskip
7
NameToCertificatepourrait vous aider, consultez la tls.Configdocumentation: golang.org/pkg/crypto/tls/#Config
cyberdelia
1
Voici un exemple d'ajout de pools de certificats CA personnalisés: golang.org/pkg/crypto/tls/#example_Dial Vous pouvez également l'utiliser dans un client HTTP.
Bitbored
7
Beaucoup de gens finissent ici en essayant de désactiver les vérifications de nom d'hôte (ne désactivant pas complètement les vérifications de certificats). C'est la mauvaise réponse. Go vous oblige à définir le ServerName dans la configuration tls pour qu'il corresponde au CN de l'hôte auquel vous vous connectez, s'il ne s'agit pas du nom DNS auquel vous vous êtes connecté. InsecureSkipVerify n'est pas plus sécurisé qu'un simple telnet sur le port. Il n'y a PAS d'authentification avec ce paramètre. User ServerName à la place!
Rob
8
Attention: un transport créé comme celui-ci utilise des valeurs nulles pour la plupart de ses champs et perd donc toutes ses valeurs par défaut . Comme le suggère la réponse ci-dessous , vous voudrez peut-être les copier. Nous avons eu beaucoup de plaisir à comprendre pourquoi nous manquions de descripteurs de fichiers , car nous avons perdu le fichierDialer.Timeout .
mknecht
26

Voici un moyen de le faire sans perdre les paramètres par défaut du DefaultTransport, et sans avoir besoin de la fausse demande selon le commentaire de l'utilisateur.

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},
}

METTRE À JOUR

Chemin plus court:

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

Bonne manière (à partir de Go 1.13) (fournie par la réponse ci-dessous ):

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

avertissement : à des fins de test / développement uniquement. Sinon, procédez à vos risques et périls !!!

Jonathan Lin
la source
ne serait-il pas plus facile de simplement copier les paramètres de transport par défaut en utilisant mytransportsettings := &(*http.DefaultTransport.(*http.Transport)), puis de modifier simplement la configuration du client TLS mytransportsettings.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}?
TheDiveO
Ça vaut le coup d'essayer, je pense que j'essayais de m'assurer de ne pas modifier le vrai DefaultTransport
Jonathan Lin
1
cela garantit de faire une copie superficielle, comme vous l'avez fait, ce qui est suffisant pour le cas d'utilisation discuté. Je déploie en fait cela dans un code de travail.
TheDiveO le
19

Toutes ces réponses sont fausses! Ne pas utiliser InsecureSkipVerifypour traiter un CN qui ne correspond pas au nom d'hôte. Les développeurs de Go ont été imprudemment catégoriques sur le fait de ne pas désactiver les vérifications de nom d'hôte (qui ont des utilisations légitimes - tunnels, nats, certificats de cluster partagés, etc.), tout en ayant quelque chose qui semble similaire mais qui ignore complètement la vérification des certificats. Vous devez savoir que le certificat est valide et signé par un certificat de confiance. Mais dans les scénarios courants, vous savez que le CN ne correspondra pas au nom d'hôte avec lequel vous vous êtes connecté. Pour ceux -ci , mis ServerNamesur tls.Config. Si tls.Config.ServerName== remoteServerCN, la vérification du certificat réussira. Voici ce que tu veux. InsecureSkipVerifysignifie qu'il n'y a PAS d'authentification; et c'est mûr pour un homme du milieu; vaincre l'objectif de l'utilisation de TLS.

Il y a une utilisation légitime pour InsecureSkipVerify: l'utiliser pour se connecter à un hôte et récupérer son certificat, puis se déconnecter immédiatement. Si vous configurez votre code à utiliser InsecureSkipVerify, c'est généralement parce que vous ne l'avez pas défini ServerNamecorrectement (il devra provenir d'une variable d'environnement ou quelque chose - ne vous inquiétez pas de cette exigence ... faites-le correctement).

En particulier, si vous utilisez des certificats clients et que vous comptez sur eux pour l'authentification, vous avez essentiellement une fausse connexion qui ne se connecte plus. Refusez le code qui le fait InsecureSkipVerify, ou vous apprendrez ce qui ne va pas à la dure!

Rob
la source
1
Connaissez-vous un site sur lequel je peux tester cela? golang org ne renvoie plus d'erreur.
topskip
3
Cette réponse est terriblement trompeuse. Si vous acceptez un certificat valablement signé quel que soit le nom d'hôte, vous n'obtenez toujours pas une véritable sécurité. Je peux facilement obtenir un certificat valide pour tous les domaines que je contrôle; si vous allez simplement spécifier mon nom d'hôte pour le contrôle TLS, vous perdez la validation que je suis vraiment qui je dis que je suis. À ce stade, le fait que le certificat soit «légitime» n'a pas d'importance; c'est toujours faux et vous êtes toujours vulnérable à l'homme du milieu.
Daniel Farrell
5
Dans une entreprise où vous ne faites confiance à aucune des autorités de certification commerciales (c'est-à-dire si elles sont en dehors des États-Unis par exemple) et que vous remplacez par une autorité de certification pour votre entreprise, il vous suffit de valider qu'il s'agit de l'un de vos certificats. La vérification du nom d'hôte concerne les situations où les utilisateurs s'attendent à ce que le nom d'hôte corresponde, mais le DNS n'est pas sécurisé. Dans l'entreprise, vous vous connectez généralement à un cluster de machines où il n'est pas possible que le nom d'hôte / IP corresponde, car il s'agit de clones exacts d'une machine générée sous de nouvelles adresses IP. Le nom dns trouve le cert et cert est l'id, pas le nom dns.
Rob
3
l'idée est que le code client ne fait confiance qu'à l'autorité de certification d'entreprise. il est en fait beaucoup plus sécurisé que le système CA actuellement utilisé. Dans ce cas, rien (Thawte, Versign, etc.) ... n'est fiable. Juste le CA que nous gérons. Les navigateurs Web ont d'énormes listes de confiance. Les services qui se parlent n'ont qu'une seule autorité de certification dans leur fichier de confiance.
Rob
1
merci @Rob J'ai une configuration identique où nos services ne font confiance qu'au même CA unique et à rien d'autre. Ce sont des informations vitales et beaucoup d'aide.
user99999991
8

La bonne façon de procéder si vous souhaitez conserver les paramètres de transport par défaut est maintenant (à partir de Go 1.13):

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

Transport.Clone fait une copie complète du transport. De cette façon, vous n'avez pas à vous soucier de manquer de nouveaux champs ajoutés à la Transportstructure au fil du temps.

Bogdan Popa
la source
7

Si vous souhaitez utiliser les paramètres par défaut du package http, vous n'avez donc pas besoin de créer un nouvel objet Transport et client, vous pouvez modifier pour ignorer la vérification du certificat comme ceci:

tr := http.DefaultTransport.(*http.Transport)
tr.TLSClientConfig.InsecureSkipVerify = true
Cornel Damian
la source
7
Faire cela entraîneraitpanic: runtime error: invalid memory address or nil pointer dereference
OscarRyz
6
Si vous n'utilisez pas le demandeur http par défaut avant cela, vous devez forcer une fausse demande pour que le transport par défaut soit initialisé
Cornel Damian
1
cela ne désactive pas les vérifications de nom d'hôte. il désactive toutes les vérifications de certificat. Go vous oblige à définir le ServerName égal au CN dans le certificat de ce à quoi vous vous connectez. InsecureSkipVerify se connectera à un serveur MITM non autorisé qui prétend transmettre aux services réels. La SEULE utilisation légitime d'un InsecureSkipVerify est de récupérer le certificat de l'extrémité distante et de se déconnecter immédiatement.
Rob
Ce n'est correct que si vous spécifiez ÉGALEMENT un VerifyPeerCertificate. Si vous venez de définir InsecureSkipVerify, cela ne vérifie pas du tout. Mais un VerifyPeerCertificate a été ajouté afin que vous puissiez réécrire le chèque pour faire ce dont vous avez besoin. Vous voudrez peut-être ignorer le nom d'hôte, ou peut-être même la date d'expiration. Google pour diverses implémentations de VerifyPeerCertificate qui font cela.
Rob le
0

En général, le domaine DNS de l'URL DOIT correspondre au sujet du certificat du certificat.

Autrefois, cela pouvait être soit en définissant le domaine comme cn du certificat, soit en définissant le domaine comme un autre nom de sujet.

La prise en charge de cn a été abandonnée pendant longtemps (depuis 2000 dans la RFC 2818 ) et le navigateur Chrome ne regardera même plus le cn.Aujourd'hui, vous devez avoir le domaine DNS de l'URL comme autre nom de sujet.

RFC 6125 qui interdit de vérifier le cn si SAN pour DNS Domain est présent, mais pas si SAN pour IP Address est présent. La RFC 6125 répète également que cn est obsolète, ce qui était déjà dit dans la RFC 2818. Et la présence du forum du navigateur de l'autorité de certification qui, en combinaison avec la RFC 6125, signifie essentiellement que cn ne sera jamais vérifié pour le nom de domaine DNS.

jwilleke
la source