¿Deshabilitar el respaldo SSL y usar solo TLS para conexiones salientes en .NET? (Mitigación de caniche)

106

Estoy tratando de mitigar nuestra vulnerabilidad al ataque Poodle SSL 3.0 Fallback . Nuestros administradores ya han comenzado a deshabilitar SSL a favor de TLS para las conexiones entrantes a nuestros servidores. Y también hemos aconsejado a nuestro equipo que desactive SSL en sus navegadores web. Ahora estoy viendo nuestra base de código .NET, que inicia conexiones HTTPS con varios servicios a través de System.Net.HttpWebRequest . Creo que estas conexiones podrían ser vulnerables a un ataque MITM si permiten el retroceso de TLS a SSL. Esto es lo que he determinado hasta ahora. ¿Alguien podría volver a comprobar esto para verificar que estoy en lo cierto? Esta vulnerabilidad es nueva, por lo que todavía no he visto ninguna orientación de Microsoft sobre cómo mitigarla en .NET:

  1. Los protocolos permitidos para la clase System.Net.Security.SslStream, que sustenta la comunicación segura en .NET, se configuran globalmente para cada AppDomain mediante la propiedad System.Net.ServicePointManager.SecurityProtocol .

  2. El valor predeterminado de esta propiedad en .NET 4.5 es Ssl3 | Tls(aunque no puedo encontrar documentación para respaldarlo). SecurityProtocolType es una enumeración con el atributo Flags, por lo que es un OR bit a bit de esos dos valores. Puede verificar esto en su entorno con esta línea de código:

    Console.WriteLine (System.Net.ServicePointManager.SecurityProtocol.ToString ());

  3. Esto debería cambiarse a solo Tls, o quizás Tls12, antes de iniciar cualquier conexión en su aplicación:

    System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls;

  4. Importante: dado que la propiedad admite varios indicadores bit a bit, supongo que SslStream no recurrirá automáticamente a otros protocolos no especificados durante el protocolo de enlace. De lo contrario, ¿cuál sería el punto de admitir múltiples banderas?

Actualización en TLS 1.0 vs 1.1 / 1.2:

Según el experto en seguridad de Google, Adam Langley, más tarde se descubrió que TLS 1.0 era vulnerable a POODLE si no se implementaba correctamente , por lo que debería considerar pasar a TLS 1.2 exclusivamente.

Actualización para .NET Framework 4.7 y superior:

Como lo menciona el profesor Von Lemongargle a continuación, a partir de la versión 4.7 de .NET Framework, no es necesario utilizar este truco, ya que la configuración predeterminada permitirá al sistema operativo elegir la versión del protocolo TLS más segura. Consulte las mejores prácticas de seguridad de la capa de transporte (TLS) con .NET Framework para obtener más información.

Jordan Rieger
fuente
1
Con respecto al punto 2 anterior: consulte Referenceource.microsoft.com/#System/net/System/Net/… SslProtocols "Default = Ssl3 | Tls"
Dai Bok
1
@Dai Bok La opción predeterminada ahora es SystemDefault docs.microsoft.com/en-us/dotnet/api/…
MarwaAhmad
@MarwaAhmad si ese es el caso, la referencia del código fuente en mi enlace no refleja el valor predeterminado (48 | 192). Si se establece en none (0), debería volver a los valores predeterminados del sistema. Esto me huele y probablemente probaría esto antes de hacer cambios, ya que puede provocar una configuración incorrecta ... y desactivar los protocolos en el marco .net incorrecto ...
Dai Bok

Respuestas:

137

Estamos haciendo lo mismo. Para admitir solo TLS 1.2 y no protocolos SSL, puede hacer esto:

System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

SecurityProtocolType.Tls es solo TLS 1.0, no todas las versiones de TLS.

Como un lado: si desea verificar que su sitio no permite conexiones SSL, puede hacerlo aquí (no creo que esto se vea afectado por la configuración anterior, tuvimos que editar el registro para obligar a IIS a usar TLS para conexiones entrantes): https://www.ssllabs.com/ssltest/index.html

Para deshabilitar SSL 2.0 y 3.0 en IIS, consulte esta página: https://www.sslshopper.com/article-how-to-disable-ssl-2.0-in-iis-7.html

Eddie Fletcher
fuente
Correcto. También es una buena idea admitir las versiones más nuevas de TLS: preparación para el futuro.
Jordan Rieger
1
Y para beneficio de otros, la edición del registro que mencionaste también se puede realizar con estos pasos: serverfault.com/a/637263/98656 . En Windows Server 2003 a 2012 R2, los protocolos están controlados por banderas en HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ SecurityProviders \ Schannel \ Protocols. Para deshabilitar SSLv3, cree una subclave en la ubicación anterior llamada 'SSL 3.0' y, debajo de ella, una subclave llamada 'Servidor' y, debajo, un valor DWORD llamado 'Habilitado', establecido en 0. También debe deshabilitar SSL 2.0 del mismo modo.
Jordan Rieger
4
@ScotterMonkey Solo necesita configurar System.Net.ServicePointManager.SecurityProtocol si está iniciando conexiones salientes desde código .NET, por ejemplo, conectándose a un servicio web o API desde un código personalizado que se ejecuta en su servidor. Si solo está ejecutando un sitio web simple y solo acepta conexiones entrantes de los navegadores, entonces la corrección del registro es suficiente.
Jordan Rieger
7
Nota: Aparece SecurityProtocolType.Tls11y los SecurityProtocolType.Tls12valores de enumeración solo están disponibles en ASP.net 4.5 y versiones posteriores. No estoy seguro de qué tendríamos que hacer con bases de código más antiguas que se ejecutan en 2.0 si TLS 1.0 se queda en el camino.
Sam
1
@AnishV Pones esa línea de código en la inicialización de tu aplicación, antes de cualquier código que inicie una conexión SSL / TLS saliente.
Jordan Rieger
23

La respuesta de @Eddie Loeffen parece ser la respuesta más popular a esta pregunta, pero tiene algunos efectos negativos a largo plazo. Si revisa la página de documentación de System.Net.ServicePointManager.SecurityProtocol aquí, la sección de comentarios implica que la fase de negociación debería abordar esto (y forzar el protocolo es una mala práctica porque en el futuro, TLS 1.2 también se verá comprometido). Sin embargo, no estaríamos buscando esta respuesta si lo hiciera.

Investigando, parece que se requiere el protocolo de negociación ALPN para llegar a TLS1.2 en la fase de negociación. Tomamos eso como nuestro punto de partida y probamos versiones más nuevas del marco .Net para ver dónde comienza el soporte. Descubrimos que .Net 4.5.2 no admite la negociación con TLS 1.2, pero .Net 4.6 sí.

Entonces, aunque forzar TLS1.2 hará el trabajo ahora, le recomiendo que actualice a .Net 4.6 en su lugar. Dado que se trata de un problema de PCI DSS para junio de 2016, la ventana es corta, pero el nuevo marco es una mejor respuesta.

ACTUALIZACIÓN: Trabajando a partir de los comentarios, construí esto:

ServicePointManager.SecurityProtocol = 0;    
foreach (SecurityProtocolType protocol in SecurityProtocolType.GetValues(typeof(SecurityProtocolType)))
    {
        switch (protocol)
        {
            case SecurityProtocolType.Ssl3:
            case SecurityProtocolType.Tls:
            case SecurityProtocolType.Tls11:
                break;
            default:
                ServicePointManager.SecurityProtocol |= protocol;
            break;
        }
    }

Para validar el concepto, utilicé SSL3 y TLS1.2 juntos y ejecuté el código dirigido a un servidor que solo admite TLS 1.0 y TLS 1.2 (1.1 está deshabilitado). Con los protocolos or'd, parece conectarse bien. Si cambio a SSL3 y TLS 1.1, no se pudo conectar. Mi validación usa HttpWebRequest de System.Net y solo llama a GetResponse (). Por ejemplo, probé esto y fallé:

        HttpWebRequest request = WebRequest.Create("https://www.contoso.com/my/web/resource") as HttpWebRequest;
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls11;
        request.GetResponse();

mientras esto funcionó:

        HttpWebRequest request = WebRequest.Create("https://www.contoso.com/my/web/resource") as HttpWebRequest;
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12;
        request.GetResponse();

Esto tiene una ventaja sobre forzar TLS 1.2 en el sentido de que, si el marco .Net se actualiza para que haya más entradas en el Enum, el código las admitirá tal cual. Tiene una desventaja sobre el simple uso de .Net 4.6 en que 4.6 usa ALPN y debería admitir nuevos protocolos si no se especifica ninguna restricción.

Editar 4/29/2019: Microsoft publicó este artículo en octubre pasado. Tiene una muy buena sinopsis de su recomendación de cómo se debe hacer esto en las distintas versiones de .net framework.

Profesor Von Lemongargle
fuente
3
Interesante. Parece que la página de documentación se actualizó junto con el cambio de MSDN a ".NET Framework (versión actual) " y ahora explica que "no se muestra ningún valor predeterminado para esta propiedad, a propósito". Quizás una versión más preparada para el futuro de la respuesta aceptada sería consultar primero la propiedad para recuperar la lista de protocolos permitidos y luego eliminar los que su aplicación considera inseguros (es decir, algo menos que TLS 1.2) en lugar de agregar explícitamente solo el los que considere seguros. Esto no excluiría nuevas versiones en el futuro.
Jordan Rieger
Sería bueno que el programa elimine los protocolos de una lista que genera el sistema, pero no veo una forma de hacerlo en la API actual. La única opción parece ser forzar el protocolo específico, lo que no veo por qué alguien querría hacer. Desafortunadamente, sin el soporte de ALPN, parece ser la única forma de hacer que funcione una conexión TLS1.2.
Prof Von Lemongargle
1
Debería ser posible recorrer todas las enumeraciones SecurityProtocolType, ver cuáles están presentes en la enumeración de banderas ServicePointManager.SecurityProtocol (es un OR lógico de todos los indicadores establecidos, por lo que puede probar cada uno con un AND), y luego construir un nuevo lista de ellos con los que no desea eliminar. Luego combine esos en una enumeración y establezca la propiedad con eso.
Jordan Rieger
Estaba releyendo tu código de enumeración / bucle, y creo que es incorrecto. El operador lógico | = dará como resultado que la propiedad incluya los valores predeterminados que se establecieron inicialmente en la propiedad, en lugar de incluir solo Tls12 y superior. Para solucionarlo, debe inicializar una variable de enumeración SecurityProtocolType con un valor de 0, hacer el bucle | = contra esa variable y luego asignarla a la propiedad ServicePointManager.SecurityProtocol.
Jordan Rieger
7
¿A alguien más le parece una locura que .NET no negocie automáticamente la versión de protocolo más alta de la que es capaz?
Raman
5

@watson

En los formularios de Windows está disponible, en la parte superior de la clase poner

  static void Main(string[] args)
    {
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
       //other stuff here
    }

dado que Windows es de un solo hilo, es todo lo que necesita, en caso de que sea un servicio, debe colocarlo justo encima de la llamada al servicio (ya que no se sabe en qué hilo estará).

using System.Security.Principal 

también es necesario.

DirtyHowi
fuente
5

Si tiene curiosidad sobre los protocolos que admite .NET, puede probar HttpClient en https://www.howsmyssl.com/

// set proxy if you need to
// WebRequest.DefaultWebProxy = new WebProxy("http://localhost:3128");

File.WriteAllText("howsmyssl-httpclient.html", new HttpClient().GetStringAsync("https://www.howsmyssl.com").Result);

// alternative using WebClient for older framework versions
// new WebClient().DownloadFile("https://www.howsmyssl.com/", "howsmyssl-webclient.html");

El resultado es condenatorio:

Su cliente está usando TLS 1.0, que es muy antiguo, posiblemente susceptible al ataque BEAST, y no tiene las mejores suites de cifrado disponibles. Las adiciones como AES-GCM y SHA256 para reemplazar MD5-SHA-1 no están disponibles para un cliente TLS 1.0, así como para muchos conjuntos de cifrado más modernos.

Como explica Eddie anteriormente, puede habilitar mejores protocolos manualmente:

System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11; 

No sé por qué usa malos protocolos listos para usar. Parece una mala elección de configuración, equivalente a un error de seguridad importante (apuesto a que muchas aplicaciones no cambian el valor predeterminado). ¿Cómo podemos denunciarlo?

Coronel Panic
fuente
5

Tuve que lanzar el equivalente entero para evitar el hecho de que todavía estoy usando .NET 4.0

System.Net.ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
/* Note the property type  
   [System.Flags]
   public enum SecurityProtocolType
   {
     Ssl3 = 48,
     Tls = 192,
     Tls11 = 768,
     Tls12 = 3072,
   } 
*/
CZahrobsky
fuente
Esto funciona, pero .NET 4.0 no es compatible con TLS 1.2, sin embargo .NET 4.5 y versiones superiores son compatibles con TLS 1.2. Entonces, puede agregar este código (o agregar bit a bit O para agregar soporte para la negociación TLS 1.2) a su aplicación .NET 4.0 y compilarlo, pero deberá implementar el código en .NET 4.5 o superior. blogs.perfficient.com/microsoft/2016/04/tsl-1-2-and-net-support
rboy
Vea también esto, el código .NET 4.0 funcionará bien en una versión superior de .NET, incluidos .NET 4.5 y .NET 4.6 stackoverflow.com/questions/33378902/…
rboy
La conversión de enteros funcionó para enviar TLS 1.2. El valor de enumeración Tls12 simplemente no existe en .NET 4.0
CZahrobsky
Dos puntos distintos. Vea mi comentario. Puede poner el valor, pero si su versión de tiempo de ejecución Net es 4.0, fallará. Puede compilar con este indicador, pero las versiones de .NET en tiempo de ejecución deben ser 4.5 o superiores, ya que los componentes base en .NET 4.0 no admiten los cifrados necesarios para TLS 1.2 (consulte los enlaces)
rboy
2
Hay varias revisiones para versiones anteriores de tiempo de ejecución de .NET para agregar compatibilidad con TLS 1.2. por ejemplo , support.microsoft.com/en-us/help/3154518/… , CZahrobsky también podría haber estado usando esto en la actualización de otoño de Win10, que también incluía la revisión.
tono silencioso
1

Encontré que la solución más simple es agregar dos entradas de registro de la siguiente manera (ejecutar esto en un símbolo del sistema con privilegios de administrador):

reg add HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319 /v SchUseStrongCrypto /t REG_DWORD /d 1 /reg:32

reg add HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319 /v SchUseStrongCrypto /t REG_DWORD /d 1 /reg:64

Estas entradas parecen afectar la forma en que .NET CLR elige un protocolo al realizar una conexión segura como cliente.

Hay más información sobre esta entrada de registro aquí:

https://docs.microsoft.com/en-us/security-updates/SecurityAdvisories/2015/2960358#suggested-actions

Esto no solo es más simple, sino que, asumiendo que funciona para su caso, es mucho más robusto que una solución basada en código, que requiere que los desarrolladores rastreen el protocolo y el desarrollo y actualicen todo su código relevante. Con suerte, se pueden realizar cambios de entorno similares para TLS 1.3 y posteriores, siempre que .NET siga siendo lo suficientemente tonto como para no elegir automáticamente el protocolo más alto disponible.

NOTA : Aunque, de acuerdo con el artículo anterior, se supone que esto solo debe deshabilitar RC4, y uno no pensaría que esto cambiaría si el cliente .NET puede usar TLS1.2 + o no, por alguna razón tiene esto efecto.

NOTA : Como señaló @Jordan Rieger en los comentarios, esta no es una solución para POODLE, ya que no deshabilita los protocolos más antiguos a - simplemente permite que el cliente trabaje con protocolos más nuevos, por ejemplo, cuando un servidor parcheado ha deshabilitado el antiguo Protocolos. Sin embargo, con un ataque MITM, obviamente un servidor comprometido ofrecerá al cliente un protocolo más antiguo, que el cliente usará felizmente.

TODO : Intente deshabilitar el uso del lado del cliente de TLS1.0 y TLS1.1 con estas entradas de registro, sin embargo, no sé si las bibliotecas de cliente http de .NET respetan estas configuraciones o no:

https://docs.microsoft.com/en-us/windows-server/security/tls/tls-registry-settings#tls-10

https://docs.microsoft.com/en-us/windows-server/security/tls/tls-registry-settings#tls-11

Raman
fuente
La configuración del registro como alternativa a un código sería excelente, pero ¿estas configuraciones realmente deshabilitan TLS 1.0 y 1.1 a favor de permitir solo conexiones de cliente usando TLS 1.2 y superior? Según el enlace, parece que solo deshabilita RC4 en TLS. Creo que el ataque de Poodle es más amplio que eso.
Jordan Rieger
@JordanRieger Estas entradas de registro permiten que un cliente .NET se conecte a un servidor que tiene los protocolos más antiguos deshabilitados para mitigar POODLE. Sin estos, el cliente arrojará un error ya que el cliente .NET insiste estúpidamente en usar un protocolo más antiguo incluso cuando el servidor solicita uno más nuevo. Todas las apuestas están canceladas si su servidor aún permite conexiones usando los protocolos más antiguos. En teoría, los protocolos más antiguos deberían desactivarse. Me pregunto si las mismas entradas de registro que permiten deshabilitarlas en el servidor (por ejemplo, nartac.com/Products/IISCrypto ) también funcionarían para el cliente.
Raman
En las entradas de registro que manipula nartac, hay configuraciones separadas para (cuando actúa como) cliente y (cuando actúa como) servidor. Sin embargo, no recuerdo si su interfaz se distingue. ¿Sabes qué versiones de .net admiten este truco de registro?
Prof Von Lemongargle
@Raman, el punto de mi pregunta, sin embargo, era mitigar la vulnerabilidad POODLE cuando su cliente se conecta a un servidor que usted no controla. La vulnerabilidad permitirá a un atacante MITM degradar el protocolo a una versión anterior de TLS o SSL que puede ser pirateada. Deshabilitar RC4 no es suficiente. Esta configuración de registro puede ser útil para ciertos casos, pero no para el escenario de mi pregunta.
Jordan Rieger
1
@JordanRieger estuvo de acuerdo. Actualicé el texto con algunas observaciones y pensamientos adicionales.
Raman