Práticas recomendadas de TLS/SSL
TLS (Transport Layer Security) é um protocolo criptográfico projetado para proteger a comunicação entre dois computadores através da internet. O protocolo TLS é exposto no .NET através da SslStream classe.
Este artigo apresenta as práticas recomendadas para configurar a comunicação segura entre cliente e servidor e pressupõe o uso do .NET. Para obter práticas recomendadas com o .NET Framework, consulte Práticas recomendadas de TLS (Transport Layer Security) com o .NET Framework.
Selecione a versão TLS
Embora seja possível especificar a versão do protocolo TLS a ser usado por meio da EnabledSslProtocols propriedade, é recomendável adiar para as configurações do sistema operacional usando None value (este é 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 receba 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 pacotes de codificação
SslStream
permite que os usuários especifiquem quais pacotes de codificação podem ser negociados pelo handshake TLS por meio da CipherSuitesPolicy classe. Assim como nas versões TLS, recomenda-se deixar o sistema operacional decidir quais são os melhores pacotes de codificação para negociar e, portanto, é recomendado evitar o uso CipherSuitesPolicydo .
Nota
CipherSuitesPolicy não é suportado no Windows e as tentativas de instanciá-lo farão com que NotSupportedException sejam lançadas.
Especificar um certificado de servidor
Ao autenticar como um servidor, SslStream requer uma X509Certificate2 instância. Recomenda-se sempre usar uma X509Certificate2 instância que também contenha a chave privada.
Há várias maneiras pelas quais um certificado de servidor pode ser passado para SslStream:
- Diretamente como um parâmetro para SslStream.AuthenticateAsServerAsync ou via SslServerAuthenticationOptions.ServerCertificate propriedade
- A partir de um retorno de chamada de seleção na SslServerAuthenticationOptions.ServerCertificateSelectionCallback propriedade
- Ao passar um SslStreamCertificateContext na SslServerAuthenticationOptions.ServerCertificateContext propriedade
A abordagem recomendada é usar a SslServerAuthenticationOptions.ServerCertificateContext propriedade. Quando o certificado é obtido por uma das outras duas maneiras, uma SslStreamCertificateContext instância é criada internamente pela SslStream implementação. Criar um SslStreamCertificateContext envolve a construção de uma X509Chain operação que é uma CPU intensiva. É mais eficiente criar uma SslStreamCertificateContext vez e reutilizá-la para várias SslStream instâncias.
A reutilização SslStreamCertificateContext de instâncias também permite recursos adicionais, como a retomada da sessão TLS em servidores Linux.
Validação personalizada X509Certificate
Há certos cenários em que o procedimento de validação de certificado padrão não é adequado e alguma lógica de validação personalizada é necessária. 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, consulte Confiança de certificado personalizada.
Confiança de certificado personalizado
Ao encontrar um certificado que não foi emitido por nenhuma das autoridades de certificação confiáveis pela máquina (incluindo certificados autoassinados), o procedimento de validação de certificado padrão 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 X509ChainPolicyarquivo . 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 pelo 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 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;
}
Fixação do 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 fixação de certificado. O trecho 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 solicitar e validar certificados de cliente. Os certificados podem conter a extensão AIA (Authority Information Access), 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 para o 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 entrar em contato com servidores externos ao criar e validar o X509Chain pode expor o aplicativo a ataques de negação de serviço se os servidores externos forem lentos para responder. Portanto, os aplicativos de servidor devem configurar o comportamento de X509Chain compilação usando o CertificateChainPolicy.