Как отключить резервный SSL и использовать только TLS для исходящих соединений в.NET? (Смягчение пуделя)

Я пытаюсь уменьшить нашу уязвимость к атаке Poodle SSL 3.0 Fallback. Наши администраторы уже начали отключать SSL в пользу TLS для входящих подключений к нашим серверам. И мы также посоветовали нашей команде отключить SSL в своих веб-браузерах. Сейчас я смотрю на нашу кодовую базу.NET, которая инициирует HTTPS-соединения с различными сервисами через System.Net.HttpWebRequest. Я считаю, что эти соединения могут быть уязвимы для атаки MITM, если они позволяют переход от TLS к SSL. Вот что я определил до сих пор. Может кто-нибудь еще раз проверить это, чтобы убедиться, что я прав? Эта уязвимость является новой, поэтому мне еще предстоит увидеть какие-либо рекомендации от Microsoft по ее устранению в.NET:

  1. Разрешенные протоколы для класса System.Net.Security.SslStream, лежащего в основе безопасной связи в.NET, устанавливаются глобально для каждого AppDomain с помощью свойства System.Net.ServicePointManager.SecurityProtocol.

  2. Значение по умолчанию этого свойства в.NET 4.5: Ssl3 | Tls (хотя я не могу найти документацию, подтверждающую это.) SecurityProtocolType - это перечисление с атрибутом Flags, так что это побитовое ИЛИ этих двух значений. Вы можете проверить это в своей среде с помощью этой строки кода:

    ЕЫпе (System.Net.ServicePointManager.SecurityProtocol.ToString());

  3. Это должно быть изменено на просто Tls или возможно Tls12 перед тем, как инициировать какие-либо подключения в вашем приложении:

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

  4. Важное замечание : Поскольку свойство поддерживает несколько побитовых флагов, я предполагаю, что SslStream не будет автоматически переходить на другие неуказанные протоколы во время рукопожатия. Иначе, какой смысл поддерживать несколько флагов?

Обновление на TLS 1.0 против 1.1/1.2:

По словам эксперта по безопасности Google Адама Лэнгли, TLS 1.0 позже был признан уязвимым для POODLE, если он не реализован правильно, поэтому вам следует рассмотреть возможность перехода исключительно на TLS 1.2.

6 ответов

Решение

Мы делаем то же самое. Для поддержки только TLS 1.2 и никаких протоколов SSL вы можете сделать это:

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

SecurityProtocolType.Tls - это только TLS 1.0, а не все версии TLS.

В качестве стороны: если вы хотите проверить, что ваш сайт не разрешает SSL-соединения, вы можете сделать это здесь (я не думаю, что это повлияет на вышеуказанную настройку, нам пришлось отредактировать реестр, чтобы IIS использовал TLS для входящих подключений): https://www.ssllabs.com/ssltest/index.html

Чтобы отключить SSL 2.0 и 3.0 в IIS, см. Эту страницу: https://www.sslshopper.com/article-how-to-disable-ssl-2.0-in-iis-7.html

@ Эдди Лоффен, кажется, самый популярный ответ на этот вопрос, но он имеет некоторые плохие долгосрочные последствия. Если вы просматриваете страницу документации для System.Net.ServicePointManager.SecurityProtocol, то здесь раздел замечаний подразумевает, что фаза согласования должна просто решить эту проблему (и принудительное использование протокола является плохой практикой, поскольку в будущем TLS 1.2 также будет скомпрометирован). Тем не менее, мы не будем искать этот ответ, если он это сделал.

Исследования показывают, что протокол согласования ALPN требуется для доступа к TLS1.2 на этапе согласования. Мы взяли это за отправную точку и попробовали новые версии.Net Framework, чтобы увидеть, с чего начинается поддержка. Мы обнаружили, что.Net 4.5.2 не поддерживает согласование с TLS 1.2, но.Net 4.6 поддерживает.

Таким образом, даже если принудительное выполнение TLS1.2 завершит работу сейчас, я рекомендую вместо этого перейти на.Net 4.6. Поскольку это проблема PCI DSS на июнь 2016 года, окно короткое, но новая структура - лучший ответ.

ОБНОВЛЕНИЕ: Работая от комментариев, я построил это:

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

Чтобы проверить концепцию, я объединил SSL3 и TLS1.2 и запустил код, предназначенный для сервера, который поддерживает только TLS 1.0 и TLS 1.2 (1.1 отключен). С протоколами or'd, кажется, нормально соединяется. Если я перехожу на SSL3 и TLS 1.1, это не удалось подключиться. Моя проверка использует HttpWebRequest из System.Net и просто вызывает GetResponse(). Например, я попробовал это и потерпел неудачу:

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

пока это работало:

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

Это имеет преимущество перед форсированием TLS 1.2, заключающееся в том, что если платформа.Net обновляется, чтобы в Enum было больше записей, они будут поддерживаться кодом как есть. Он имеет недостаток по сравнению только с использованием.Net 4.6 в том, что 4.6 использует ALPN и должен поддерживать новые протоколы, если не указано никаких ограничений.

Мне пришлось привести целочисленный эквивалент, чтобы обойти тот факт, что я все еще использую.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,
   } 
*/

@watson

На окнах формы есть в наличии, вверху класса ставь

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

Поскольку Windows является однопоточным, это все, что вам нужно, в случае, если это сервис, вам нужно поместить его прямо над вызовом сервиса (так как неизвестно, в каком потоке вы будете).

using System.Security.Principal 

также необходимо.

Если вам интересно, какие протоколы поддерживает.NET, вы можете попробовать HttpClient на 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");

Результат проклятый:

Ваш клиент использует TLS 1.0, который очень старый, возможно подверженный атаке BEAST и не имеет лучших доступных наборов шифров. Такие дополнения, как AES-GCM и SHA256 для замены MD5-SHA-1, недоступны для клиента TLS 1.0, а также для многих других современных наборов шифров.

Как объясняет Эдди выше, вы можете включить лучшие протоколы вручную:

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

Я не знаю, почему он использует плохие протоколы из коробки. Это кажется плохим выбором установки, равносильным серьезной ошибке безопасности (держу пари, многие приложения не меняют настройки по умолчанию). Как мы можем сообщить об этом?

Я обнаружил, что самое простое решение - добавить две записи реестра следующим образом (запустите это в командной строке с правами администратора):

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

Кажется, что эти записи влияют на то, как.NET CLR выбирает протокол при создании безопасного соединения в качестве клиента.

Подробнее об этой записи в реестре здесь:

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

Мало того, что это проще, но при условии, что оно работает для вашего случая, гораздо более надежное, чем решение на основе кода, которое требует от разработчиков отслеживать протокол и разработку и обновлять весь свой соответствующий код. Надеемся, что подобные изменения среды могут быть сделаны для TLS 1.3 и более поздних версий, пока.NET остается достаточно тупым, чтобы автоматически не выбирать самый высокий доступный протокол.

ПРИМЕЧАНИЕ. Несмотря на то, что, согласно статье выше, предполагается, что это только отключает RC4, и никто не думал бы, что это изменится, разрешено ли клиенту.NET использовать TLS1.2+ или нет, по какой-то причине он имеет это эффект.

ПРИМЕЧАНИЕ: Как отметил @Jordan Rieger в комментариях, это не решение для POODLE, поскольку он не отключает старые протоколы a - он просто позволяет клиенту работать с более новыми протоколами, например, когда исправленный сервер отключил старый протоколы. Однако в случае атаки MITM очевидно, что скомпрометированный сервер предложит клиенту более старый протокол, который клиент с радостью будет использовать.

TODO: Попробуйте отключить использование TLS1.0 и TLS1.1 на стороне клиента с этими записями реестра, однако я не знаю, уважают ли клиентские библиотеки.NET http эти параметры или нет:

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

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

Другие вопросы по тегам