web-dev-qa-db-fra.com

Meilleures pratiques pour l'utilisation de ServerCertificateValidationCallback

Je travaille sur un projet qui utilise une communication HTTP entre deux serveurs principaux. Les serveurs utilisent des certificats X509 pour l'authentification. Il va sans dire que lorsque le serveur A (client) établit une connexion avec le serveur B (serveur), il y a une erreur de validation SSL/TLS, car les certificats utilisés ne proviennent pas d'une autorité tierce de confiance.

Normalement, la façon de le gérer est d'utiliser ServicePointManager.ServerCertificateValidationCallback, tel que:

ServicePointManager.ServerCertificateValidationCallback += 
        (sender, cert, chain, error) =>
{
    return cert.GetCertHashString() == "xxxxxxxxxxxxxxxx";
};

Cette approche fonctionne, sauf qu'elle n'est pas idéale. Ce qu'il fait essentiellement, c'est remplacer la procédure de validation pour CHAQUE requête http effectuée par l'application. Donc, si une autre classe tente d'exécuter la requête HTTP, elle échouera. De plus, si une autre classe remplace ServicePointManager.ServerCertificateValidationCallback à ses propres fins, puis ma communication commence à échouer soudainement.

La seule solution qui me vient à l'esprit est de créer un AppDomain distinct pour effectuer les requêtes HTTP du client. Cela fonctionnerait, mais vraiment - c'est idiot de devoir le faire uniquement pour que l'on puisse effectuer des requêtes HTTP. Les frais généraux seront stupéfiants.

Dans cet esprit, quelqu'un a-t-il recherché s'il existe une meilleure pratique dans .NET, qui permettrait d'accéder aux services Web, tout en gérant la validation SSL/TLS du client sans affecter les autres clients Web?

29
galets

Une méthodologie acceptable (sûre) fonctionnant dans .NET 4.5+ consiste à utiliser HttpWebRequest.ServerCertificateValidationCallback. L'affectation de ce rappel à une instance spécifique de la demande modifiera la logique de validation uniquement pour la demande, sans influencer les autres demandes.

var request = (HttpWebRequest)WebRequest.Create("https://...");
request.ServerCertificateValidationCallback += 
        (sender, cert, chain, error) =>
{
    return cert.GetCertHashString() == "xxxxxxxxxxxxxxxx";
};
33
galets

Une alternative pour le code qui n'utilise pas HttpWebRequest et pour les environnements où vous ne pouvez pas installer de certificats approuvés dans le magasin de certificats: Vérifiez le paramètre d'erreur du rappel, qui contiendra toute erreur détectée avant le rappel. De cette façon, vous pouvez ignorer les erreurs pour des chaînes de hachage spécifiques, mais accepter tout de même d'autres certificats qui passent la validation.

ServicePointManager.ServerCertificateValidationCallback += 
    (sender, cert, chain, error) =>
{
    if (cert.GetCertHashString() == "xxxxxxxxxxxxxxxx")
    {
        return true;
    }
    else
    {
       return error == SslPolicyErrors.None;
    }
};

Référence: https://msdn.Microsoft.com/en-us/library/system.net.security.remotecertificatevalidationcallback (v = vs.110) .aspx

Notez que cela affectera toujours les autres instances de client Web dans le même domaine d'application (ils accepteront tous la chaîne de hachage spécifiée), mais au moins cela ne bloquera pas les autres certificats.

12
ckarras

L'approche directe pour ce scénario doit être d'installer les deux certificats auto-générés dans les magasins racine approuvés sur les machines clientes. Vous recevrez un avertissement de sécurité lorsque vous effectuez cette opération car les certificats ne peuvent pas être authentifiés avec Thawte ou similaire, mais après cela, une communication sécurisée régulière devrait fonctionner. IIRC, vous devez installer la version complète (clé publique et clé privée) dans une racine de confiance pour que cela fonctionne.