Procedure consigliate per TLS/SSL

TLS (Transport Layer Security) è un protocollo crittografico progettato per proteggere la comunicazione tramite Internet tra due computer. Il protocollo TLS viene esposto in .NET tramite la classe SslStream.

Questo articolo illustra le procedure consigliate per configurare la comunicazione sicura tra client e server e presuppone l'uso di .NET. Per le procedure consigliate con .NET Framework, vedi Procedure consigliate per Transport Layer Security (TLS) con .NET Framework.

Selezionare la versione TLS

Sebbene sia possibile specificare la versione del protocollo TLS da usare tramite la proprietà EnabledSslProtocols, è consigliabile rinviare le impostazioni del sistema operativo usando il valore None (impostazione predefinita).

Rinviando la decisione al sistema operativo, si usa automaticamente la versione più recente di TLS disponibile e si consente all'applicazione di rilevare le modifiche dopo gli aggiornamenti del sistema operativo. Il sistema operativo può anche impedire l'uso di versioni TLS che non sono più considerate sicure.

Selezionare suite di crittografia

SslStream consente agli utenti di specificare quali suite di crittografia possono essere negoziate dall'handshake TLS tramite la classe CipherSuitesPolicy. Come per le versioni di TLS, è consigliabile consentire al sistema operativo di decidere quali sono le migliori suite di crittografia con cui negoziare e, di conseguenza, è consigliabile evitare di usare CipherSuitesPolicy.

Nota

CipherSuitesPolicy non è supportato in Windows e i tentativi di crearne un'istanza causeranno la creazione di NotSupportedException.

Specificare un certificato del server

Quando si esegue l'autenticazione come server, SslStream richiede un'istanza di X509Certificate2. È consigliabile usare sempre un'istanza X509Certificate2 che contiene anche la chiave privata.

Esistono diversi modi con cui passare un certificato del server a SslStream:

L'approccio consigliato è di usare la proprietà SslServerAuthenticationOptions.ServerCertificateContext. Quando il certificato viene ottenuto in uno degli altri due modi, un'istanza SslStreamCertificateContext viene creata internamente dall'implementazione SslStream. La creazione di un SslStreamCertificateContext comporta la creazione di un X509Chain, operazione che richiede un utilizzo intensivo della CPU. È più efficiente creare un SslStreamCertificateContext sola volta e riutilizzarla per più istanze SslStream.

Il riutilizzo delle istanze SslStreamCertificateContext abilita anche funzionalità aggiuntive, ad esempio la ripresa della sessione TLS nei server Linux.

Convalida X509Certificate personalizzata

Esistono alcuni scenari in cui la procedura di convalida del certificato predefinita non è adeguata ed è necessaria una logica di convalida personalizzata. Le parti della logica di convalida possono essere personalizzate specificando SslClientAuthenticationOptions.CertificateChainPolicy o SslServerAuthenticationOptions.CertificateChainPolicy. In alternativa, è possibile fornire una logica completamente personalizzata tramite la proprietà <System.Net.Security.SslClientAuthenticationOptions.RemoteCertificateValidationCallback>. Per altre informazioni, vedi Attendibilità del certificato personalizzato.

Attendibilità del certificato personalizzato

Quando viene rilevato dal computer un certificato che non è stato emesso da alcuna autorità di certificazione attendibile (inclusi i certificati autofirmati), la procedura di convalida del certificato predefinita avrà esito negativo. Un modo possibile per risolvere questo problema consiste nell'aggiungere i certificati dell'autorità di certificazione necessari all'archivio attendibile del computer. Ciò, tuttavia, potrebbe influire su altre applicazioni nel sistema e non è sempre possibile.

La soluzione alternativa consiste nello specificare certificati radice trusted personalizzati tramite X509ChainPolicy. Per specificare un elenco di attendibilità personalizzato da usare anziché l'elenco di attendibilità del sistema durante la convalida, considera l'esempio seguente:

SslClientAuthenticationOptions clientOptions = new();

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

I client configurati con i criteri precedenti accettano solo certificati attendibili di customIssuerCert.

Ignorare errori di convalida specifici

Considera un dispositivo IoT senza un orologio permanente. Dopo l'accensione, l'orologio del dispositivo inizierebbe molti anni prima e, pertanto, tutti i certificati sarebbero considerati "non ancora validi". Considera il codice seguente, che mostra un'implementazione di callback di convalida ignorando le violazioni del periodo di 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;
}

Aggiunta di certificati

Un'altra situazione in cui è necessaria la convalida personalizzata dei certificati è quando i client prevedono che i server usino un certificato specifico o il certificato di un piccolo set di certificati noti. Questa procedura è nota come aggiunta di certificati. Il frammento di codice seguente mostra un callback di convalida che verifica che il server presenti un certificato con una specifica chiave pubblica nota.

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

Considerazioni sulla convalida dei certificati client

Le applicazioni server devono prestare attenzione quando richiedono e convalidano i certificati client. I certificati possono contenere l'estensione AIA (Authority Information Access), che specifica dove è possibile scaricare il certificato dell'autorità di certificazione. Il server può quindi tentare di scaricare il certificato dell'autorità di certificazione dal server esterno durante la compilazione di X509Chain per il certificato client. Analogamente, i server potrebbero dover contattare i server esterni per assicurarsi che il certificato client non sia stato revocato.

La necessità di contattare i server esterni durante la compilazione e la convalida di X509Chain può esporre l'applicazione ad attacchi Denial of Service, se i server esterni sono lenti a rispondere. Pertanto, le applicazioni server devono configurare il comportamento di compilazione X509Chain usando CertificateChainPolicy.