Best practices voor TLS/SSL

TLS (Transport Layer Security) is een cryptografisch protocol dat is ontworpen om de communicatie tussen twee computers via internet te beveiligen. Het TLS-protocol wordt via de SslStream klasse in .NET weergegeven.

In dit artikel vindt u aanbevolen procedures voor het instellen van beveiligde communicatie tussen client en server en wordt ervan uitgegaan dat .NET wordt gebruikt. Zie best practices voor Transport Layer Security (TLS) met het .NET Framework.

TLS-versie selecteren

Hoewel het mogelijk is om de versie van het TLS-protocol op te geven die via de EnabledSslProtocols eigenschap moet worden gebruikt, is het raadzaam om de instellingen van het besturingssysteem uit te stellen met behulp van None waarde (dit is de standaardinstelling).

Als u de beslissing voor het besturingssysteem uitstelt, wordt automatisch de meest recente versie van TLS gebruikt die beschikbaar is en kan de toepassing wijzigingen ophalen na upgrades van het besturingssysteem. Het besturingssysteem kan ook het gebruik van TLS-versies voorkomen die niet langer als veilig worden beschouwd.

Coderingssuites selecteren

SslStream staat gebruikers toe om op te geven welke coderingssuites kunnen worden onderhandeld door de TLS-handshake via de CipherSuitesPolicy klasse. Net als bij TLS-versies is het raadzaam om het besturingssysteem te laten beslissen welke de beste versleutelingsalgoritmes zijn om mee te onderhandelen, en daarom wordt het aanbevolen om het gebruik van CipherSuitesPolicy te vermijden.

Opmerking

CipherSuitesPolicy wordt niet ondersteund in Windows en pogingen om het te instantiëren, zal ertoe leiden NotSupportedException dat er een fout optreedt.

Een lokaal certificaat opgeven

Voor verificatie als server SslStream is altijd een certificaat vereist. Wanneer u zich als client authenticeert, verstrekt u ook een certificaat als de server daar om vraagt voor wederzijdse TLS (mTLS). In beide rollen moet het certificaat een X509Certificate2 exemplaar zijn dat de persoonlijke sleutel bevat.

Recente .NET versies verwerken de server- en clientzijde symmetrisch, zodat de volgende richtlijnen van toepassing zijn of de toepassing wordt geverifieerd als een server, een client of beide.

U kunt het certificaat op verschillende manieren aan SslStream aanleveren.

Wanneer u zich verifieert als een server:

Wanneer u zich verifieert als een client:

Opmerking

De ClientCertificateContext eigenschap is beschikbaar vanaf .NET 8.

Gebruik de eigenschap certificaatcontext (ServerCertificateContext of ClientCertificateContext) voor betere prestaties. Wanneer u het certificaat op een van de andere manieren verstrekt, maakt SslStream intern een SslStreamCertificateContext aan. Het maken van de context bouwt een X509Chain, wat een CPU-intensieve bewerking is, dus het is efficiënter om de context eenmaal te maken en opnieuw te gebruiken in meerdere SslStream exemplaren.

Als u een SslStreamCertificateContext exemplaar opnieuw gebruikt, kunt u ook extra functies zoals TLS-sessiehervatting op Linux-servers inschakelen.

Tussenliggende certificaten verzenden naar de peer

Wanneer een tussenliggende certificeringsinstantie het lokale certificaat uitgeeft, kan de peer mogelijk niet de volledige certificaatketen bouwen, tenzij de handshake de tussenliggende certificaten bevat. Als u deze tussenliggende items wilt verzenden, maakt u een SslStreamCertificateContext met de Create methode en geeft u de tussenliggende certificaten door in de additionalCertificates parameter:

X509Certificate2 leafCertificate = GetLeafCertificate();
X509Certificate2Collection intermediates = GetIntermediateCertificates();

SslStreamCertificateContext certificateContext =
    SslStreamCertificateContext.Create(leafCertificate, intermediates);

// When you authenticate as a server.
serverOptions.ServerCertificateContext = certificateContext;

// When you authenticate as a client for mutual TLS.
clientOptions.ClientCertificateContext = certificateContext;

De context werkt op dezelfde manier voor beide rollen. Aan de clientzijde is de certificaatcontext de aanbevolen manier om tussenliggende certificaten te verzenden, omdat het alternatief — het toevoegen van de tussenliggende certificaten aan de certificaatopslag van de computer of gebruiker — van invloed is op elke toepassing op het systeem.

Aangepaste X509Certificate validatie

Er zijn bepaalde scenario's waarin de standaardvalidatieprocedure voor certificaten niet voldoende is en een aantal aangepaste validatielogica is vereist. Onderdelen van de validatielogica kunnen worden aangepast door op te geven SslClientAuthenticationOptions.CertificateChainPolicy of SslServerAuthenticationOptions.CertificateChainPolicy. U kunt ook volledig aangepaste logica opgeven via de eigenschap <System.Net.Security.SslClientAuthenticationOptions.RemoteCertificateValidationCallback>. Zie Aangepast certificaatvertrouwen voor meer informatie.

Aangepast certificaatvertrouwen

Wanneer u een certificaat tegenkomt dat niet is uitgegeven door een van de certificeringsinstanties die worden vertrouwd door de computer (inclusief zelfondertekende certificaten), mislukt de standaardprocedure voor certificaatvalidatie. Een mogelijke manier om dit op te lossen is door de benodigde uitgevende certificaten toe te voegen aan de vertrouwde opslag van de computer. Dat kan echter van invloed zijn op andere toepassingen op het systeem en is niet altijd mogelijk.

De alternatieve oplossing is het opgeven van aangepaste vertrouwde basiscertificaten via een X509ChainPolicy. Als u een aangepaste vertrouwenslijst wilt opgeven die wordt gebruikt in plaats van de lijst met systeemvertrouwensrelaties tijdens de validatie, kunt u het volgende voorbeeld bekijken:

SslClientAuthenticationOptions clientOptions = new();

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

Clients die zijn geconfigureerd met het voorgaande beleid, accepteren alleen certificaten die worden vertrouwd door customIssuerCert.

Specifieke validatiefouten negeren

Overweeg een IoT-apparaat zonder permanente klok. Na het inschakelen zou de klok van het apparaat vele jaren in het verleden beginnen en daarom worden alle certificaten beschouwd als 'nog niet geldig'. Bekijk de volgende code waarin een validatie-callback-implementatie wordt weergegeven, waarbij schendingen van de geldigheidsperiode worden genegeerd.

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

Certificaat pinnen

Een andere situatie waarin validatie van aangepaste certificaten nodig is, is wanneer clients verwachten dat servers een specifiek certificaat of een certificaat van een kleine set bekende certificaten gebruiken. Deze praktijk wordt certificaatpinning genoemd. Het volgende codefragment toont een validatieaanroep die controleert of de server een certificaat met een specifieke openbare sleutel presenteert.

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

Overwegingen voor validatie van clientcertificaten

Servertoepassingen moeten voorzichtig zijn bij het vereisen en valideren van clientcertificaten. Certificaten kunnen de AIA-extensie (Authority Information Access) bevatten die aangeeft waar het certificaat van de uitgever kan worden gedownload. De server kan daarom proberen om het certificaat van de uitgever te downloaden van externe server bij het bouwen van het X509Chain voor het clientcertificaat. Op dezelfde manier moeten servers mogelijk contact opnemen met externe servers om ervoor te zorgen dat het clientcertificaat niet is ingetrokken.

De noodzaak om contact op te maken met externe servers bij het bouwen en valideren van de X509Chain toepassing kan worden blootgesteld aan Denial of Service-aanvallen als de externe servers traag reageren. Daarom moeten servertoepassingen het gedrag van het X509Chain gebouw configureren met behulp van de CertificateChainPolicy.

Opmerking

Vanaf .NET 11 schakelt SslStream standaard AIA-certificaatdownloads uit bij het valideren van clientcertificaten als server. Als er geen aangepaste CertificateChainPolicy certificaten worden opgegeven, probeert de server geen ontbrekende tussenliggende certificaten op te halen via AIA. Voor meer informatie, zie SslStream server-side AIA-certificaatdownloads standaard uitgeschakeld.