Bonnes pratiques TLS/SSL

TLS (Transport Layer Security) est un protocole de chiffrement conçu pour sécuriser la communication entre deux ordinateurs via Internet. Le protocole TLS est exposé dans .NET via la classe SslStream.

Cet article présente les meilleures pratiques pour configurer la communication sécurisée entre le client et le serveur et suppose l’utilisation de .NET. Pour connaître les meilleures pratiques avec .NET Framework, consultez Meilleures pratiques du protocole TLS (Transport Layer Security) avec .NET Framework.

Sélectionner la version de TLS

Bien qu’il soit possible de spécifier la version du protocole TLS à utiliser via la propriété EnabledSslProtocols, il est recommandé de différer les paramètres du système d’exploitation à l’aide de la valeur None (il s’agit de la valeur par défaut).

Différer la décision du système d’exploitation utilise automatiquement la version la plus récente de TLS disponible et permet à l’application de récupérer les modifications après les mises à niveau du système d’exploitation. Le système d’exploitation peut également empêcher l’utilisation de versions TLS qui ne sont plus considérées comme sécurisées.

Sélectionner des suites de chiffrement

SslStream permet aux utilisateurs de spécifier les suites de chiffrement qui peuvent être négociées par l’établissement d’une liaison TLS via la classe CipherSuitesPolicy. Comme avec les versions TLS, il est recommandé de laisser le système d’exploitation décider quelles sont les meilleures suites de chiffrement avec lesquelles négocier, et par conséquent, il est recommandé d’éviter d’utiliser CipherSuitesPolicy.

Notes

CipherSuitesPolicy n’est pas pris en charge sur Windows et les tentatives d’instanciation entraînent la levée d’une NotSupportedException.

Spécifier un certificat de serveur

Lors de l’authentification en tant que serveur, SslStream nécessite une instance X509Certificate2. Il est recommandé d’utiliser toujours une instance X509Certificate2 qui contient également la clé privée.

Il existe plusieurs façons pour un certificat de serveur d’être transféré à SslStream :

L’approche recommandée consiste à utiliser la propriété SslServerAuthenticationOptions.ServerCertificateContext. Lorsque le certificat est obtenu de l’une des deux autres façons, une instance SslStreamCertificateContext est créée en interne par l’implémentation de SslStream. La création d’un(e) SslStreamCertificateContext implique la création d’une opération X509Chain gourmande en ressources d'UC. Il est plus efficace de créer un(e) SslStreamCertificateContext à la fois et de le réutiliser pour plusieurs instances SslStream.

La réutilisation des instances SslStreamCertificateContext active également des fonctionnalités supplémentaires telles que la reprise de session TLS sur les serveurs Linux.

Validation X509Certificate personnalisée

Il existe certains scénarios dans lesquels la procédure de validation de certificat par défaut n’est pas adéquate et une logique de validation personnalisée est requise. Les parties de la logique de validation peuvent être personnalisées en spécifiant SslClientAuthenticationOptions.CertificateChainPolicy ou SslServerAuthenticationOptions.CertificateChainPolicy. Vous pouvez également fournir une logique complètement personnalisée via la propriété <System.Net.Security.SslClientAuthenticationOptions.RemoteCertificateValidationCallback>. Pour plus d’informations, consultez Approbation de certificat personnalisée.

Approbation de certificat personnalisée

Lorsque vous rencontrez un certificat qui n’a pas été émis par les autorités de certification approuvées par l’ordinateur (y compris les certificats auto-signés), la procédure de validation de certificat par défaut échoue. Pour résoudre ce problème, vous pouvez ajouter les certificats d’émetteur nécessaires au magasin approuvé de l’ordinateur. Toutefois, cela peut affecter d’autres applications sur le système et ce n’est pas toujours possible.

La solution alternative consiste à spécifier des certificats racines approuvés personnalisés via un X509ChainPolicy. Pour spécifier une liste d’approbation personnalisée qui sera utilisée au lieu de la liste d’approbations système lors de la validation, considérez l’exemple suivant :

SslClientAuthenticationOptions clientOptions = new();

clientOptions.CertificateChainPolicy = new X509ChainPolicy()
{
    TrustMode = X509ChainTrustMode.CustomRootTrust,
    CustomTrustStore =
    {
        customIssuerCert
    }
};

Les clients configurés avec la stratégie précédente acceptent uniquement les certificats approuvés par customIssuerCert.

Ignorer des erreurs de validation spécifiques

Songez à un appareil IoT sans horloge persistante. Après la mise sous tension, l’horloge de l’appareil démarrerait plusieurs années dans le passé et, par conséquent, tous les certificats seraient considérés comme « non encore valides ». Considérez le code suivant qui montre une implémentation de rappel de validation ignorant les violations de période de validité.

static bool CustomCertificateValidationCallback(
    object sender,
    X509Certificate? certificate,
    X509Chain? chain,
    SslPolicyErrors sslPolicyErrors)
{
    // Anything that would have been accepted by default is OK
    if (sslPolicyErrors == SslPolicyErrors.None)
    {
        return true;
    }
    
    // If there is something wrong other than a chain processing error, don't trust it.
    if (sslPolicyErrors != SslPolicyErrors.RemoteCertificateChainErrors)
    {
        return false;
    }
    
    Debug.Assert(chain is not null);

    // If the reason for RemoteCertificateChainError is that the chain built empty, don't trust it.
    if (chain.ChainStatus.Length == 0)
    {
        return false;
    }

    foreach (X509ChainStatus status in chain.ChainStatus)
    {
        // If an error other than `NotTimeValid` (or `NoError`) is present, don't trust it.
        if ((status.Status & ~X509ChainStatusFlags.NotTimeValid) != X509ChainStatusFlags.NoError)
        {
            return false;
        }
    }

    return true;
}

Épinglage de certificat

Une autre situation où la validation de certificat personnalisée est nécessaire se présente lorsque les clients s’attendent à ce que les serveurs utilisent un certificat spécifique ou un certificat d’un petit ensemble de certificats connus. Cette pratique est appelée épinglage de certificat. L’extrait de code suivant montre un rappel de validation qui vérifie que le serveur présente un certificat avec une clé publique connue spécifique.

static bool CustomCertificateValidationCallback(
    object sender,
    X509Certificate? certificate,
    X509Chain? chain,
    SslPolicyErrors sslPolicyErrors)
{
    // If there is something wrong other than a chain processing error, don't trust it.
    if ((sslPolicyErrors & ~SslPolicyErrors.RemoteCertificateChainErrors) != 0)
    {
        return false;
    }
    
    Debug.Assert(certificate is not null);

    const string ExpectedPublicKey =
        "3082010A0282010100C204ECF88CEE04C2B3D850D57058CC9318EB5C" +
        "A86849B022B5F9959EB12B2C763E6CC04B604C4CEAB2B4C00F80B6B0" +
        "F972C98602F95C415D132B7F71C44BBCE9942E5037A6671C618CF641" +
        "42C546D31687279F74EB0A9D11522621736C844C7955E4D16BE8063D" +
        "481552ADB328DBAAFF6EFF60954A776B39F124D131B6DD4DC0C4FC53" +
        "B96D42ADB57CFEAEF515D23348E72271C7C2147A6C28EA374ADFEA6C" +
        "B572B47E5AA216DC69B15744DB0A12ABDEC30F47745C4122E19AF91B" +
        "93E6AD2206292EB1BA491C0C279EA3FB8BF7407200AC9208D98C5784" +
        "538105CBE6FE6B5498402785C710BB7370EF6918410745557CF9643F" +
        "3D2CC3A97CEB931A4C86D1CA850203010001";

    return certificate.GetPublicKeyString().Equals(ExpectedPublicKey);
}

Considérations relatives à la validation des certificats clients

Les applications serveur doivent être prudentes lors de la réquisition et de la validation des certificats clients. Les certificats peuvent contenir l’extension AIA (Authority Information Access) qui spécifie où le certificat de l’émetteur peut être téléchargé. Le serveur peut donc tenter de télécharger le certificat de l’émetteur à partir d’un serveur externe lors de la génération de X509Chain pour le certificat client. De même, les serveurs peuvent avoir besoin de contacter des serveurs externes pour s’assurer que le certificat client n’a pas été révoqué.

La nécessité de contacter des serveurs externes lors de la génération et de la validation du/de la X509Chain peut exposer l’application à des attaques par déni de service si les serveurs externes mettent du temps à répondre. Par conséquent, les applications serveur doivent configurer le comportement de génération de X509Chain à l’aide du/de la CertificateChainPolicy.