Compartilhar via


Práticas recomendadas de TLS/SSL

O TLS (Transport Layer Security) é um protocolo criptográfico projetado para proteger a comunicação entre dois computadores pela Internet. O protocolo TLS é exposto no .NET por meio da classe SslStream.

Este artigo apresenta as práticas recomendadas para configurar a comunicação segura entre o cliente e o servidor e pressupõe o uso do .NET. Para obter práticas recomendadas com .NET Framework, confira Melhores práticas de protocolo TLS com o .NET Framework.

Selecionar a versão do TLS

Embora seja possível especificar a versão do protocolo TLS a ser usada por meio da propriedade EnabledSslProtocols, é recomendável adiar para as configurações do sistema operacional usando o valor None (o padrão).

Adiar a decisão para o sistema operacional usa automaticamente a versão mais recente do TLS disponível e permite que o aplicativo selecione as alterações após as atualizações do sistema operacional. O sistema operacional também pode impedir o uso de versões TLS que não são mais consideradas seguras.

Selecionar conjuntos de criptografia

O SslStream permite que os usuários especifiquem quais conjuntos de criptografia podem ser negociados pelo handshake do TLS por meio da classe CipherSuitesPolicy. Assim como nas versões TLS, é recomendável deixar o sistema operacional decidir quais são os melhores conjuntos de cifras para negociar e, portanto, é recomendável evitar o uso de CipherSuitesPolicy.

Observação

O CipherSuitesPolicy não tem suporte no Windows e as tentativas de instanciá-lo farão com que NotSupportedException seja lançado.

Especificar um certificado de servidor

Ao autenticar como um servidor, o SslStream requer uma instância do X509Certificate2. É recomendável sempre usar uma instância do X509Certificate2 que também contenha a chave privada.

Há várias maneiras pelas quais um certificado de servidor pode ser passado para SslStream:

A abordagem recomendada é usar a propriedade SslServerAuthenticationOptions.ServerCertificateContext. Quando o certificado é obtido por uma das outras duas formas, uma instância de SslStreamCertificateContext é criada internamente pela implementação deSslStream. A criação de um SslStreamCertificateContext envolve a criação de um X509Chain que é uma operação intensiva de CPU. É mais eficiente criar um SslStreamCertificateContext uma vez e reutilizá-lo para várias instâncias de SslStream.

Reutilizar instâncias de SslStreamCertificateContext também permite o uso de recursos adicionais, como retomada da sessão TLS em servidores Linux.

Validação de X509Certificate personalizada

Há certos cenários em que o procedimento de validação de certificado padrão não é adequado e é necessário aplicar alguma lógica de validação personalizada. Partes da lógica de validação podem ser personalizadas especificando SslClientAuthenticationOptions.CertificateChainPolicy ou SslServerAuthenticationOptions.CertificateChainPolicy. Como alternativa, a lógica completamente personalizada pode ser fornecida por meio da propriedade <System.Net.Security.SslClientAuthenticationOptions.RemoteCertificateValidationCallback>. Para obter mais informações, confira Confiança de certificado personalizada.

Confiança de certificado personalizada

Ao encontrar um certificado que não foi emitido por nenhuma autoridade de certificação confiável para a máquina (incluindo certificados autoassinados), o procedimento padrão de validação do certificado falhará. Uma maneira possível de resolver isso é adicionar os certificados de emissor necessários ao armazenamento confiável da máquina. Isso, no entanto, pode afetar outros aplicativos no sistema e nem sempre é possível.

A solução alternativa é especificar certificados raiz confiáveis personalizados por meio de um X509ChainPolicy. Para especificar uma lista de confiança personalizada que será usada em vez da lista de confiança do sistema durante a validação, considere o seguinte exemplo:

SslClientAuthenticationOptions clientOptions = new();

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

Os clientes configurados com a política anterior só aceitariam certificados confiáveis por customIssuerCert.

Ignorar erros de validação específicos

Considere um dispositivo IoT sem um relógio persistente. Depois de ligado, o relógio do dispositivo começaria muitos anos no passado e, portanto, todos os certificados seriam considerados "ainda não válidos". Considere o código a seguir que mostra uma implementação de retorno de chamada de validação ignorando as violações do período de validade.

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

Anexação de certificado

Outra situação em que a validação de certificado personalizado é necessária é quando os clientes esperam que os servidores usem um certificado específico ou um certificado de um pequeno conjunto de certificados conhecidos. Essa prática é conhecida como anexação de certificado. O snippet de código a seguir mostra um retorno de chamada de validação que verifica se o servidor apresenta um certificado com uma chave pública conhecida específica.

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

Considerações para validação de certificado de cliente

Os aplicativos de servidor precisam ter cuidado ao exigir e validar certificados de cliente. Os certificados podem conter a extensão AIA (Acesso à Informação de Autoridade) que especifica onde o certificado do emissor pode ser baixado. O servidor pode, portanto, tentar baixar o certificado do emissor do servidor externo ao criar o X509Chain do certificado do cliente. Da mesma forma, os servidores podem precisar entrar em contato com servidores externos para garantir que o certificado do cliente não tenha sido revogado.

A necessidade de contatar servidores externos ao compilar e validar o X509Chain poderá expor o aplicativo a ataques de negação de serviço se os servidores externos tiverem lentidão na resposta. Portanto, os aplicativos de servidor devem configurar o comportamento de compilação de X509Chain usando o CertificateChainPolicy.