Comment ignorer la vérification du certificat lorsque SSL

132

J'essaie de trouver un moyen d'ignorer la vérification du certificat lorsque je demande une ressource Https, jusqu'à présent, j'ai trouvé un article utile sur Internet.

Mais j'ai encore un problème. Veuillez revoir mon code. Je ne comprends tout simplement pas ce que signifie le code ServicePointManager.ServerCertificateValidationCallback.

Quand cette méthode déléguée sera-t-elle appelée? Et encore une question, à quel endroit dois-je écrire ce code? Avant d' ServicePointManager.ServerCertificateValidationCallbackexécuter ou avant Stream stream = request.GetRequestStream()?

public HttpWebRequest GetRequest()
{
    CookieContainer cookieContainer = new CookieContainer();

    // Create a request to the server
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_remoteUrl);

    #region Set request parameters

    request.Method = _context.Request.HttpMethod;
    request.UserAgent = _context.Request.UserAgent;
    request.KeepAlive = true;
    request.CookieContainer = cookieContainer;
    request.PreAuthenticate = true;
    request.AllowAutoRedirect = false;

    #endregion

    // For POST, write the post data extracted from the incoming request
    if (request.Method == "POST")
    {
        Stream clientStream = _context.Request.InputStream;
        request.ContentType = _context.Request.ContentType;
        request.ContentLength = clientStream.Length;

        ServicePointManager.ServerCertificateValidationCallback = delegate(
            Object obj, X509Certificate certificate, X509Chain chain, 
            SslPolicyErrors errors)
            {
                return (true);
            };

            Stream stream = request.GetRequestStream();

            ....
        }

        ....

        return request;
    }
}   
Joe.wang
la source
Possible duplication de C # Ignorer les erreurs de certificat?
Matyas

Réponses:

67

Puisqu'il n'y a qu'un seul ServicePointManager global , la définition de ServicePointManager.ServerCertificateValidationCallback produira le résultat que toutes les demandes suivantes hériteront de cette stratégie. Puisqu'il s'agit d'un "paramètre" global, il serait préférable de le définir dans la méthode Application_Start de Global.asax .

La définition du rappel remplace le comportement par défaut et vous pouvez créer vous-même une routine de validation personnalisée.

Sani Singh Huttunen
la source
Qu'en est-il pour un client qui n'a pas Global.asax? J'appelle un service REST fonctionnant sur le réseau local à partir d'un appareil portable.
B. Clay Shannon
3
La question est spécifique à HttpWebRequest. Si vous utilisez d'autres moyens, vous devrez regarder dans la documentation comment y parvenir.
Sani Singh Huttunen
J'utilise WebRequest, qui est converti en HttpWebRequest, tel que: ((HttpWebRequest) request) .Accept = contentType;
B. Clay Shannon
Comme indiqué dans ma réponse: il est préférable de le définir dans Global.asax pas une exigence. Vous pouvez même le définir avant l'appel au service REST.
Sani Singh Huttunen
3
Voir ce lien pour une solution possible.
Sani Singh Huttunen
174

Pour toute personne intéressée par l'application de cette solution sur une base par demande, il s'agit d'une option et utilise une expression Lambda. La même expression Lambda peut également être appliquée au filtre global mentionné par blak3r. Cette méthode semble nécessiter .NET 4.5.

String url = "https://www.stackoverflow.com";
HttpWebRequest request = HttpWebRequest.CreateHttp(url);
request.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;

Dans .NET 4.0, l'expression Lambda peut être appliquée au filtre global en tant que tel

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
Adam Venezia
la source
2
Existe-t-il une solution à la demande pour FtpWebRequest?
Timo
Il semble exister bien dans mon 4.0
Nacht
Je pense que c'est de loin une meilleure solution à la variante «globale», même si bien sûr je peux comprendre pourquoi vous le voudriez. Personnellement je privilégie une fabrique de requêtes qui gère ensuite ce callback de validation. Merci Adam, belle solution.
The Senator
2
Le retour trueest quelque chose que vous pouvez faire lorsque vous expérimentez pendant le développement, mais ce n'est pas sûr. Ce devrait être un conditionnel.
Fred
1
L'utilisation (HttpWebRequest)WebRequest.Create(url)est parfaitement valable, mais sur ma box, HttpWebRequest.Create(url)existe toujours dans un projet ciblant .Net 4.6.2. Le choix du chef, mais à ce stade, HttpClientc'est probablement la meilleure API à utiliser.
Adam Venezia
53

Cela a fonctionné pour moi:

System.Net.ServicePointManager.ServerCertificateValidationCallback +=
delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
                        System.Security.Cryptography.X509Certificates.X509Chain chain,
                        System.Net.Security.SslPolicyErrors sslPolicyErrors)
    {
        return true; // **** Always accept
    };

Extrait d'ici: http://www.west-wind.com/weblog/posts/2011/Feb/11/HttpWebRequest-and-Ignoring-SSL-Certificate-Errors

blak3r
la source
17
ServicePointManager.ServerCertificateValidationCallback + = (expéditeur, certificat, chaîne, sslPolicyErrors) => true;
David Rutgos
3
Rendre toujours vrai n'est pas sûr. Il doit retourner conditionnellement vrai.
Fred
C'est par processus, donc ce n'est pas sûr.
Mikhail Orlov
25

Il existe également la solution de délégué court:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; 
Andrej Rommel
la source
3
Toujours revenir truen'est pas sûr.
Fred
7
Oui, toujours faire confiance à tous les certificats SSL n'est pas sûr par définition. Évitez de le faire si possible.
Andrej Rommel le
@AndrejRommel quel est votre chemin recommandé?
Kiquenet
2
La méthode recommandée est de créer un certificat SSL valide et de l'utiliser correctement si vous avez le contrôle sur le serveur. Nous avons fini par en créer un en utilisant letsencrypt.org.
Andrej Rommel
5

Il a été mentionné qu'avant .NET 4.5 la propriété sur la demande d'accès à son ServicePointManager n'était pas disponible.

Voici le code .NET 4.0 qui vous donnera accès à la ServicePointdemande par requête. Il ne vous donne pas accès au rappel par demande, mais il devrait vous permettre d'obtenir plus de détails sur le problème. Accédez simplement aux propriétés scvPoint.Certificate(ou ClientCertificatesi vous préférez).

WebRequest request = WebRequest.Create(uri);

// oddity: these two .Address values are not necessarily the same!
//  The service point appears to be related to the .Host, not the Uri itself.
//  So, check the .Host vlaues before fussing in the debugger.
//
ServicePoint svcPoint = ServicePointManager.FindServicePoint(uri);
if (null != svcPoint)
{
    if (!request.RequestUri.Host.Equals(svcPoint.Address.Host, StringComparison.OrdinalIgnoreCase))
    {
        Debug.WriteLine(".Address              == " + request.RequestUri.ToString());
        Debug.WriteLine(".ServicePoint.Address == " + svcPoint.Address.ToString());
    }
    Debug.WriteLine(".IssuerName           == " + svcPoint.Certificate.GetIssuerName());
}
Jesse Chisholm
la source
D'accord! Mais alors, ce PO est de savoir comment ignoreleur faire confiance, pas leur faire confiance.
Jesse Chisholm
Quoi qu'il en soit, en utilisant ServicePointje ne peux pas toujours faire confiance à tous les certificats SSL, ni ignorer tous les certificats , car je n'ai pas ServerCertificateValidationCallback délégué dans ServicePoint
Kiquenet
4

À propos, c'est la manière la moins verbeuse de désactiver toute validation de certificat dans une application donnée que je connaisse:

ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
Christian Findlay
la source
4

Plutôt que d'ajouter un rappel à ServicePointManager qui remplacera la validation de certificat globalement, vous pouvez définir le rappel sur une instance locale de HttpClient. Cette approche ne doit affecter que les appels effectués à l'aide de cette instance de HttpClient.

Voici un exemple de code montrant comment l'ignorance des erreurs de validation de certificat pour des serveurs spécifiques peut être implémentée dans un contrôleur d'API Web.

using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

public class MyController : ApiController
{

    // use this HttpClient instance when making calls that need cert errors suppressed
    private static readonly HttpClient httpClient;

    static MyController()
    {
        // create a separate handler for use in this controller
        var handler = new HttpClientHandler();

        // add a custom certificate validation callback to the handler
        handler.ServerCertificateCustomValidationCallback = ((sender, cert, chain, errors) => ValidateCert(sender, cert, chain, errors));

        // create an HttpClient that will use the handler
        httpClient = new HttpClient(handler);
    }

    protected static ValidateCert(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors)
    {

        // set a list of servers for which cert validation errors will be ignored
        var overrideCerts = new string[]
        {
            "myproblemserver",
            "someotherserver",
            "localhost"
        };

        // if the server is in the override list, then ignore any validation errors
        var serverName = cert.Subject.ToLower();
        if (overrideCerts.Any(overrideName => serverName.Contains(overrideName))) return true;

        // otherwise use the standard validation results
        return errors == SslPolicyErrors.None;
    }

}
Sheldon
la source
Cela ne fonctionnera-t-il pas uniquement pour .NET Core? (Ou chaque fois que ServerCertificateCustomValidationCallback a été ajouté à HttpClientHandler)?
phillyslick
Cette solution devrait fonctionner dans .Net Framework 4.5 et versions ultérieures ainsi que .Net Core (même si je ne l'ai pas testé dans .Net Core).
Sheldon
@Sheldon, c'était un bijou :) Utilisé dans mes appels api localhost entre une application .net et une application principale .net avec succès. Belle solution de contournement sans tout accepter.
Grand le
3

Sur la base de la réponse d'Adam et du commentaire de Rob, j'ai utilisé ceci:

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => certificate.Issuer == "CN=localhost";

qui filtre quelque peu le "ignorant". D'autres émetteurs peuvent être ajoutés au besoin bien entendu. Cela a été testé dans .NET 2.0 car nous devons prendre en charge du code hérité.

Andrej Grobler
la source
@karank Désolé pour la réponse tardive - Il peut être ajouté n'importe où avant l'appel réel, par exemple. avant d'appeler request.GetResponse (). Notez que l'émetteur peut contenir quelque chose d'autre dans votre cas.
Andrej Grobler
3

CA5386: Les outils d'analyse de vulnérabilité vous alerteront de ces codes.

Code correct:

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) =>
{
   return (sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != SslPolicyErrors.RemoteCertificateNotAvailable;
};
dev_O
la source
3

Pour .net core

using (var handler = new HttpClientHandler())
{ 
    // allow the bad certificate
    handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) => true;
    using (var httpClient = new HttpClient(handler))
    {
        await httpClient.PostAsync("the_url", null);
    }
}
Rich Hildebrand
la source
2

Exprimé explicitement ...

ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(CertCheck);

private static bool CertCheck(object sender, X509Certificate cert,
X509Chain chain, System.Net.Security.SslPolicyErrors error)
{
    return true;
}
dev_O
la source
0

En ajoutant aux réponses de Sani et blak3r, j'ai ajouté ce qui suit au code de démarrage de mon application, mais en VB:

'** Overriding the certificate validation check.
Net.ServicePointManager.ServerCertificateValidationCallback = Function(sender, certificate, chain, sslPolicyErrors) True

Semble faire l'affaire.

MCattle
la source
0

Conseil: vous pouvez également utiliser cette méthode pour suivre les certificats qui expireront bientôt. Cela peut sauver votre bacon si vous découvrez un certificat sur le point d'expirer et que vous pouvez le réparer à temps. Bon aussi pour les entreprises tierces - pour nous, c'est DHL / FedEx. DHL laisse juste un certificat expirer, ce qui nous gêne 3 jours avant Thanksgiving. Heureusement, je suis là pour le réparer ... cette fois!

    private static DateTime? _nextCertWarning;
    private static bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
    {
        if (error == SslPolicyErrors.None)
        {
            var cert2 = cert as X509Certificate2;
            if (cert2 != null)
            { 
                // If cert expires within 2 days send an alert every 2 hours
                if (cert2.NotAfter.AddDays(-2) < DateTime.Now)
                {
                    if (_nextCertWarning == null || _nextCertWarning < DateTime.Now)
                    {
                        _nextCertWarning = DateTime.Now.AddHours(2);

                        ProwlUtil.StepReached("CERT EXPIRING WITHIN 2 DAYS " + cert, cert.GetCertHashString());   // this is my own function
                    }
                }
            }

            return true;
        }
        else
        {
            switch (cert.GetCertHashString())
            {
                // Machine certs - SELF SIGNED
                case "066CF9CAD814DE2097D367F22D3A7E398B87C4D6":    

                    return true;

                default:
                    ProwlUtil.StepReached("UNTRUSTED CERT " + cert, cert.GetCertHashString());
                    return false;
            }
        }
    }
Simon_Weaver
la source
Assurez-vous de gérer la situation où votre mécanisme d'alerte peut avoir un certificat expiré - ou vous vous retrouverez avec un stackoverflow!
Simon_Weaver
C'est quoi ProwlUtil.StepReached?
Kiquenet
Désolé, c'est juste ma propre méthode pour appeler l'API Prowl qui peut envoyer des notifications sur mon téléphone. Quelle que soit la façon dont vous voulez vous connecter, c'est bien. J'aime être mis sur écoute sur mon téléphone pour des choses comme ça!
Simon_Weaver
0

Plusieurs réponses ci-dessus fonctionnent. Je voulais une approche dans laquelle je ne devais pas continuer à faire des changements de code et ne rendais pas mon code non sécurisé. Par conséquent, j'ai créé une liste blanche. La liste blanche peut être conservée dans n'importe quelle banque de données. J'ai utilisé le fichier de configuration car c'est une très petite liste.

Mon code est ci-dessous.

ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, error) => {
    return error == System.Net.Security.SslPolicyErrors.None || certificateWhitelist.Contains(cert.GetCertHashString());
};
Osa E
la source
0

Version Unity C # de cette solution:

void Awake()
{
    System.Net.ServicePointManager.ServerCertificateValidationCallback += ValidateCertification;
}

void OnDestroy()
{
    ServerCertificateValidationCallback = null;
}

public static bool ValidateCertification(object sender, X509Certificate certificate, X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
    return true;
}
Amoureux du jeu de Santiago
la source