Procedimientos recomendados de TLS/SSL
TLS (Seguridad de la capa de transporte) es un protocolo criptográfico diseñado para proteger la comunicación entre dos equipos a través de Internet. El protocolo TLS se expone en .NET a través de la clase SslStream.
En este artículo se presentan los procedimientos recomendados para configurar una comunicación segura entre el cliente y el servidor, y se supone el uso de .NET. Para conocer los procedimientos recomendados con .NET Framework, consulte Procedimientos recomendados sobre la seguridad de la capa de transporte (TLS) con .NET Framework.
Selección de la versión de TLS
Aunque es posible especificar la versión del protocolo TLS que debe usarse con la propiedad EnabledSslProtocols, se recomienda aplazar la configuración del sistema operativo usando el valor None (predeterminado).
Al aplazar la decisión del sistema operativo, se usa automáticamente la versión más reciente de TLS disponible y permite que la aplicación adopte los cambios después de las actualizaciones del sistema operativo. El sistema operativo también puede impedir el uso de versiones de TLS que ya no se consideran seguras.
Selección de conjuntos de cifrado
SslStream
permite a los usuarios especificar qué conjuntos de cifrado se pueden negociar con el protocolo de enlace TLS a través de la clase CipherSuitesPolicy. Al igual que con las versiones de TLS, se recomienda permitir que el sistema operativo decida cuáles son los mejores conjuntos de cifrado con los que negociar y, por tanto, se recomienda evitar el uso de CipherSuitesPolicy.
Nota
CipherSuitesPolicy no se admite en Windows y los intentos de crear instancias de él producirán una excepción NotSupportedException.
Especificación de un certificado de servidor
Al autenticarse como servidor, SslStream requiere una instancia de X509Certificate2. Se recomienda usar siempre una instancia de X509Certificate2 que también contenga la clave privada.
Hay varias maneras de pasar un certificado de servidor a SslStream:
- Directamente como parámetro a SslStream.AuthenticateAsServerAsync o a través de la propiedad SslServerAuthenticationOptions.ServerCertificate.
- Desde una devolución de llamada de selección en la propiedad SslServerAuthenticationOptions.ServerCertificateSelectionCallback.
- Pasando SslStreamCertificateContext en la propiedad SslServerAuthenticationOptions.ServerCertificateContext.
El enfoque recomendado es utilizar la propiedad SslServerAuthenticationOptions.ServerCertificateContext. Cuando el certificado se obtiene de una de las otras dos maneras, la implementación de SslStream crea internamente una instancia de SslStreamCertificateContext. La creación de SslStreamCertificateContext implica la creación de X509Chain, lo que supone hacer un uso intensivo de la CPU. Es más eficaz crear SslStreamCertificateContext una vez y reutilizarlo para varias instancias de SslStream.
La reutilización de instancias de SslStreamCertificateContext también permite características adicionales, como la reanudación de la sesión de TLS en servidores Linux.
Validación de X509Certificate
personalizada
Hay algunos escenarios en los que el procedimiento de validación de certificados predeterminado no es adecuado y es necesario usar cierta lógica de validación personalizada. Algunas partes de la lógica de validación se pueden personalizar especificando SslClientAuthenticationOptions.CertificateChainPolicy o SslServerAuthenticationOptions.CertificateChainPolicy. Como alternativa, se puede proporcionar lógica totalmente personalizada con la propiedad <System.Net.Security.SslClientAuthenticationOptions.RemoteCertificateValidationCallback>. Para obtener más información, consulte Confianza de certificado personalizada.
Confianza de certificado personalizada
Cuando se encuentra un certificado no emitido por ninguna de las entidades de certificación de confianza del equipo (incluidos los certificados autofirmados), se produce un error en el procedimiento de validación de certificados predeterminado. Una manera posible de resolver esto es agregar los certificados de emisor necesarios al almacén de confianza de la máquina. Sin embargo, esto podría afectar a otras aplicaciones del sistema y no siempre es posible.
La solución alternativa consiste en especificar certificados raíz de confianza personalizados a través de X509ChainPolicy. Para especificar una lista de confianza personalizada que se usará en lugar de la lista de confianza del sistema durante la validación, vea el ejemplo siguiente:
SslClientAuthenticationOptions clientOptions = new();
clientOptions.CertificateChainPolicy = new X509ChainPolicy()
{
TrustMode = X509ChainTrustMode.CustomRootTrust,
CustomTrustStore =
{
customIssuerCert
}
};
Los clientes configurados con la directiva anterior solo aceptarían los certificados de confianza de customIssuerCert
.
Omisión de errores de validación específicos
Imagine un dispositivo IoT sin un reloj persistente. Después de encenderse, el reloj del dispositivo comenzaría muchos años atrás y, por tanto, todos los certificados se considerarían "no válidos aún". Vea el siguiente código, que muestra una implementación de devolución de llamada de validación que omite las infracciones del período de validez.
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;
}
Asignación de certificados
Otra situación en la que es necesaria la validación de certificados personalizada es cuando los clientes esperan que los servidores usen un certificado específico o un certificado de un reducido conjunto de certificados conocidos. Esta práctica se conoce como asignación de certificados. El siguiente fragmento de código muestra una devolución de llamada de validación que comprueba que el servidor presenta un certificado con una clave pública conocida 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);
}
Consideraciones para la validación de certificados de cliente
Las aplicaciones de servidor deben tener cuidado al requerir y validar certificados de cliente. Los certificados pueden contener la extensión AIA (acceso a información de entidad emisora), que especifica dónde se puede descargar el certificado del emisor. Por tanto, el servidor puede intentar descargar el certificado del emisor del servidor externo al compilar X509Chain para el certificado del cliente. Del mismo modo, es posible que los servidores tengan que ponerse en contacto con servidores externos para asegurarse de que no se ha revocado el certificado del cliente.
La necesidad de ponerse en contacto con servidores externos al compilar y validar X509Chain puede exponer la aplicación a ataques de denegación de servicio si los servidores externos son lentos para responder. Por tanto, las aplicaciones de servidor deben configurar el comportamiento de compilación de X509Chain con CertificateChainPolicy.