Desativar o fallback de SSL e usar apenas TLS para conexões de saída em .NET? (Mitigação Poodle)

106

Estou tentando mitigar nossa vulnerabilidade ao ataque Poodle SSL 3.0 Fallback . Nossos administradores já começaram a desabilitar o SSL em favor do TLS para conexões de entrada para nossos servidores. E também aconselhamos nossa equipe a desabilitar o SSL em seus navegadores. Agora estou olhando nossa base de código .NET, que inicia conexões HTTPS com vários serviços por meio de System.Net.HttpWebRequest . Acredito que essas conexões podem ser vulneráveis ​​a um ataque MITM se permitirem o fallback de TLS para SSL. Aqui está o que eu determinei até agora. Alguém poderia verificar novamente para verificar se estou certo? Esta vulnerabilidade é nova, então ainda não vi nenhuma orientação da Microsoft sobre como mitigá-la no .NET:

  1. Os protocolos permitidos para a classe System.Net.Security.SslStream, que sustentam a comunicação segura em .NET, são definidos globalmente para cada AppDomain por meio da propriedade System.Net.ServicePointManager.SecurityProtocol .

  2. O valor padrão dessa propriedade no .NET 4.5 é Ssl3 | Tls(embora eu não consiga encontrar documentação para fazer backup disso). SecurityProtocolType é um enum com o atributo Flags, portanto, é um OR bit a bit desses dois valores. Você pode verificar isso em seu ambiente com esta linha de código:

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

  3. Isso deve ser alterado para apenas Tls, ou talvez Tls12, antes de iniciar qualquer conexão em seu aplicativo:

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

  4. Importante: como a propriedade oferece suporte a vários sinalizadores bit a bit, presumo que o SslStream não fará fallback automaticamente para outros protocolos não especificados durante o handshake. Caso contrário, qual seria o ponto de apoiar vários sinalizadores?

Atualização em TLS 1.0 vs 1.1 / 1.2:

De acordo com o especialista em segurança do Google Adam Langley, o TLS 1.0 foi posteriormente considerado vulnerável ao POODLE se não for implementado corretamente , então você deve considerar mudar para o TLS 1.2 exclusivamente.

Atualização para .NET Framework 4.7 e superior:

Conforme aludido pelo Prof Von Lemongargle abaixo, a partir da versão 4.7 do .NET Framework, não há necessidade de usar este hack, pois a configuração padrão permitirá que o SO escolha a versão do protocolo TLS mais segura. Consulte as práticas recomendadas de TLS (Transport Layer Security) com o .NET Framework para obter mais informações.

Jordan Rieger
fonte
1
Com relação ao ponto 2 acima: consulte referencesource.microsoft.com/#System/net/System/Net/… SslProtocols "Default = Ssl3 | Tls"
Dai Bok
1
@Dai Bok O padrão agora é a opção SystemDefault docs.microsoft.com/en-us/dotnet/api/…
MarwaAhmad
@MarwaAhmad se for esse o caso, a referência do código-fonte em meu link não reflete o padrão (48 | 192). Se definido como nenhum (0), ele deve reverter para o padrão do sistema. Isso me cheira mal e provavelmente testaria antes de fazer alterações, pois pode levar a erros de configuração ... e ao desligamento de protocolos na estrutura .net errada ...
Dai Bok

Respostas:

137

Estamos fazendo a mesma coisa. Para suportar apenas TLS 1.2 e nenhum protocolo SSL, você pode fazer o seguinte:

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

SecurityProtocolType.Tls é apenas TLS 1.0, nem todas as versões TLS.

Como um lado: se você quiser verificar se seu site não permite conexões SSL, você pode fazer isso aqui (não acho que isso será afetado pela configuração acima, tivemos que editar o registro para forçar o IIS a usar TLS para conexões de entrada): https://www.ssllabs.com/ssltest/index.html

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

Eddie Fletcher
fonte
Certo. Boa ideia para oferecer suporte às versões mais recentes de TLS também: à prova de futuro.
Jordan Rieger,
1
E para o benefício de outros, a edição do registro que você mencionou também pode ser feita com estas etapas: serverfault.com/a/637263/98656 . No Windows Server 2003 a 2012 R2, os protocolos são controlados por sinalizadores em HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ SecurityProviders \ Schannel \ Protocols. Para desabilitar SSLv3, crie uma subchave no local acima chamada 'SSL 3.0' e, abaixo dela, uma subchave chamada 'Servidor' e, lá, um valor DWORD chamado 'Ativado', definido como 0. Você também deve desativar SSL 2.0 do mesmo jeito.
Jordan Rieger
4
@ScotterMonkey Você só precisa definir System.Net.ServicePointManager.SecurityProtocol se estiver iniciando conexões de saída do código .NET, por exemplo, conectando-se a um serviço da Web ou API a partir de um código personalizado executado em seu servidor. Se você estiver executando apenas um site simples e aceitar apenas conexões de entrada de navegadores, a correção do registro é o suficiente.
Jordan Rieger
7
Nota: Parece SecurityProtocolType.Tls11e SecurityProtocolType.Tls12valores enum só estão disponíveis em ASP.net 4.5 e para cima. Não tenho certeza do que teríamos que fazer para bases de código mais antigas rodando em 2.0 se o TLS 1.0 fosse deixado de lado.
Sam
1
@AnishV Você coloca essa linha de código na inicialização do seu aplicativo, antes de qualquer código que inicie uma conexão SSL / TLS de saída.
Jordan Rieger
23

A resposta de @Eddie Loeffen parece ser a resposta mais popular para essa pergunta, mas tem alguns efeitos negativos a longo prazo. Se você revisar a página de documentação de System.Net.ServicePointManager.SecurityProtocol aqui, a seção de comentários implica que a fase de negociação deve apenas abordar isso (e forçar o protocolo é uma prática ruim porque, no futuro, o TLS 1.2 também será comprometido). No entanto, não estaríamos procurando por essa resposta se existisse.

Pesquisando, parece que o protocolo de negociação ALPN é necessário para chegar ao TLS1.2 na fase de negociação. Tomamos isso como nosso ponto de partida e testamos versões mais recentes do framework .Net para ver onde o suporte começa. Descobrimos que .Net 4.5.2 não oferece suporte à negociação para TLS 1.2, mas .Net 4.6 sim.

Portanto, embora forçar o TLS1.2 fará o trabalho agora, recomendo que você atualize para o .Net 4.6. Como este é um problema do PCI DSS de junho de 2016, a janela é curta, mas a nova estrutura é uma resposta melhor.

ATUALIZAÇÃO: Trabalhando a partir dos comentários, eu construí isto:

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 o conceito, juntei SSL3 e TLS1.2 e executei o código visando um servidor que suporta apenas TLS 1.0 e TLS 1.2 (1.1 está desabilitado). Com os protocolos OR, parece se conectar bem. Se eu mudar para SSL3 e TLS 1.1, houve falha na conexão. Minha validação usa HttpWebRequest de System.Net e apenas chama GetResponse (). Por exemplo, eu tentei isso e falhei:

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

enquanto isso funcionava:

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

Isso tem a vantagem de forçar o TLS 1.2, pois, se a estrutura .Net for atualizada para que haja mais entradas no Enum, elas serão suportadas pelo código no estado em que se encontram. Ele tem uma desvantagem em relação ao uso de .Net 4.6, pois o 4.6 usa ALPN e deve oferecer suporte a novos protocolos se nenhuma restrição for especificada.

Editar 29/04/2019 - A Microsoft publicou este artigo em outubro passado. Ele tem uma boa sinopse de sua recomendação de como isso deve ser feito nas várias versões do framework .net.

Prof Von Lemongargle
fonte
3
Interessante. Parece que a página de documentação foi atualizada junto com a mudança do MSDN para ".NET Framework (versão atual) " e agora explica que "nenhum valor padrão está listado para esta propriedade, propositalmente." Talvez uma versão mais preparada para o futuro da resposta aceita seria primeiro consultar a propriedade para recuperar a lista de protocolos permitidos e, em seguida, remover aqueles que seu aplicativo considera inseguros (ou seja, qualquer coisa menor que TLS 1.2) em vez de adicionar explicitamente apenas o aqueles que você considera seguros. Isso não excluiria novas versões no futuro.
Jordan Rieger
Seria bom fazer com que o programa removesse protocolos de uma lista que o sistema gera, mas não vejo uma maneira de fazer isso na API atual. A única opção parece ser forçar o protocolo específico, o que não vejo por que alguém desejaria fazer. Infelizmente, sem o suporte ALPN, parece ser a única maneira de fazer uma conexão TLS1.2 funcionar.
Prof Von Lemongargle,
1
Deve ser possível percorrer todos os enums SecurityProtocolType, ver quais estão presentes no enum de sinalizadores ServicePointManager.SecurityProtocol (é um OR lógico de todos os sinalizadores definidos, então você pode testar cada um com um AND) e, em seguida, construir um novo lista deles com aqueles que você não deseja remover. Em seguida, combine-os em um enum e defina a propriedade com isso.
Jordan Rieger
Eu estava relendo seu código enum / looping e acho que está incorreto. O operador lógico | = resultará na propriedade incluindo quaisquer valores padrão definidos na propriedade inicialmente, em vez de incluir apenas Tls12 e acima. Para corrigi-lo, você deve inicializar uma variável enum SecurityProtocolType com um valor 0, fazer o loop | = nessa variável e, em seguida, atribuí-la à propriedade ServicePointManager.SecurityProtocol.
Jordan Rieger
7
Alguém mais acha insano que o .NET não negocie automaticamente a versão de protocolo mais alta de que é capaz ?!
Raman
5

@watson

No windows forms está disponível, no topo da classe coloque

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

uma vez que o windows é single threaded, é tudo que você precisa; no caso de ser um serviço, você precisa colocá-lo logo acima da chamada para o serviço (já que não há como dizer em qual thread você estará).

using System.Security.Principal 

também é necessário.

DirtyHowi
fonte
5

Se você estiver curioso para saber quais protocolos .NET são compatíveis, experimente o HttpClient em 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");

O resultado é terrível:

Seu cliente está usando TLS 1.0, que é muito antigo, possivelmente suscetível ao ataque BEAST, e não tem os melhores conjuntos de criptografia disponíveis. Adições como AES-GCM e SHA256 para substituir MD5-SHA-1 não estão disponíveis para um cliente TLS 1.0, bem como muitos outros conjuntos de criptografia modernos.

Como Eddie explica acima, você pode habilitar protocolos melhores manualmente:

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

Não sei por que ele usa protocolos ruins prontos para uso. Essa parece uma escolha de configuração ruim, equivalente a um grande bug de segurança (aposto que muitos aplicativos não mudam o padrão). Como podemos relatar isso?

Coronel Panic
fonte
5

Tive que lançar o equivalente inteiro para contornar o fato de que ainda estou usando o .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
fonte
Isso funciona, mas o .NET 4.0 não oferece suporte a TLS 1.2; no entanto, o .NET 4.5 e superior oferecem suporte a TLS 1.2. Portanto, você pode adicionar este código (ou adicionar um bit a bit OU para adicionar suporte para negociação TLS 1.2) ao seu aplicativo .NET 4.0 e criá-lo, mas você precisará implantar o código no .NET 4.5 ou superior. blogs.perficient.com/microsoft/2016/04/tsl-1-2-and-net-support
rboy
Veja também, o código .NET 4.0 funcionará bem em versões superiores do .NET, incluindo .NET 4.5 e .NET 4.6 stackoverflow.com/questions/33378902/…
rboy
O elenco inteiro funcionou para o envio de TLS 1.2. O valor enum Tls12 simplesmente não existe no .net 4.0
CZahrobsky
Dois pontos diferentes. Veja meu comentário. Você pode colocar o valor, mas se a versão do tempo de execução da rede for 4.0, ele falhará. Você pode compilar com este sinalizador, mas as versões do .NET em tempo de execução precisam ser 4.5 ou superior, pois os componentes básicos do .NET 4.0 não suportam as cifras necessárias para TLS 1.2 (consulte os links)
rboy
2
Existem vários hotfixes para versões mais antigas do .NET runtime para adicionar suporte a TLS 1.2. por exemplo , support.microsoft.com/en-us/help/3154518/… , CZahrobsky também pode ter usado isso na atualização dos criadores de outono do Win10, que também incluiu o hotfix.
tom silencioso
1

Descobri que a solução mais simples é adicionar duas entradas de registro da seguinte maneira (execute isso em um prompt de comando com privilégios 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

Essas entradas parecem afetar a forma como o .NET CLR escolhe um protocolo ao fazer uma conexão segura como um cliente.

Há mais informações sobre essa entrada do registro aqui:

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

Isso não apenas é mais simples, mas presumindo que funcione para o seu caso, é muito mais robusto do que uma solução baseada em código, que requer que os desenvolvedores rastreiem o protocolo e o desenvolvimento e atualizem todos os seus códigos relevantes. Felizmente, mudanças semelhantes no ambiente podem ser feitas para o TLS 1.3 e além, desde que o .NET permaneça burro o suficiente para não escolher automaticamente o protocolo mais alto disponível.

NOTA : Mesmo que, de acordo com o artigo acima, isso só desabilite o RC4, e ninguém pensaria que isso mudaria se o cliente .NET pode usar TLS1.2 + ou não, por algum motivo ele tem isso efeito.

NOTA : Como observado por @Jordan Rieger nos comentários, esta não é uma solução para o POODLE, uma vez que não desabilita os protocolos mais antigos a - apenas permite que o cliente trabalhe com os protocolos mais novos, por exemplo, quando um servidor com patch desabilitou os mais antigos protocolos. No entanto, com um ataque MITM, obviamente um servidor comprometido oferecerá ao cliente um protocolo mais antigo, que o cliente usará com prazer.

TODO : tente desativar o uso do lado do cliente de TLS1.0 e TLS1.1 com essas entradas de registro, mas não sei se as bibliotecas de cliente HTTP do .NET respeitam essas configurações ou não:

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
fonte
As configurações do registro como uma alternativa para um código seriam ótimas, mas essas configurações realmente desabilitam o TLS 1.0 e 1.1 em favor de permitir apenas conexões de cliente usando TLS 1.2 e superior? De acordo com o link, ele parece desabilitar apenas o RC4 no TLS. Acho que o ataque do Poodle é mais amplo do que isso.
Jordan Rieger
@JordanRieger Essas entradas de registro permitem que um cliente .NET se conecte a um servidor que possui os protocolos mais antigos desabilitados para reduzir POODLE. Sem isso, o cliente gerará um erro, pois o cliente .NET insiste estupidamente em usar um protocolo mais antigo, mesmo quando o servidor está solicitando um mais novo. Todas as apostas estão canceladas se o seu servidor ainda permitir conexões usando os protocolos mais antigos. Teoricamente, os protocolos mais antigos devem ser desativados. Eu me pergunto se as mesmas entradas de registro que permitem desabilitá-los no servidor (por exemplo, nartac.com/Products/IISCrypto ) funcionariam para o cliente também?
Raman
Nas entradas de registro que o nartac manipula, há configurações separadas para (ao atuar como) cliente e (ao atuar como) servidor. Não me lembro se sua interface distingue, no entanto. Você sabe quais versões do .net oferecem suporte a esse hack do registro?
Prof Von Lemongargle
@Raman, o ponto da minha pergunta, entretanto, era atenuar a vulnerabilidade POODLE quando seu cliente está se conectando a um servidor que você não controla. A vulnerabilidade permitirá que um invasor MITM faça o downgrade do protocolo para uma versão mais antiga de TLS ou SSL que pode ser hackeada. Apenas desativar o RC4 não é suficiente. Essas configurações de registro podem ser úteis para certos casos, mas não para o cenário da minha pergunta.
Jordan Rieger
1
@JordanRieger Concordo. Atualizei o texto com algumas observações e pensamentos adicionais.
Raman de