Безопасность с помощью сетевых протоколов

Зашифрованные взаимодействия клиент-сервер используют Transport Layer Security (TLS) для защиты данных вашего приложения.

В этой статье обсуждаются передовые методы, связанные с передовыми практиками безопасных сетевых протоколов, а также соображения инфраструктуры открытых ключей (PKI) (PKI). Прочтите Обзор безопасности Android , а также Обзор разрешений для получения более подробной информации.

Концепции

Сервер с сертификатом TLS имеет открытый ключ и соответствующий закрытый ключ. Сервер использует криптографию с открытым ключом для подписи своего сертификата во время установления связи TLS.

Простое рукопожатие только доказывает, что сервер знает закрытый ключ сертификата. Чтобы решить эту ситуацию, позвольте клиенту доверять нескольким сертификатам. Данный сервер является ненадежным, если его сертификат не указан в наборе доверенных сертификатов на стороне клиента.

Однако серверы могут использовать ротацию ключей для замены открытого ключа своего сертификата на новый. Изменение конфигурации сервера требует обновления клиентского приложения. Если сервер представляет собой стороннюю веб-службу, например веб-браузер или приложение электронной почты, сложнее определить, когда обновлять клиентское приложение.

Серверы обычно полагаются на сертификаты центров сертификации (CA) для выдачи сертификатов, что обеспечивает более стабильную конфигурацию на стороне клиента с течением времени. Центр сертификации подписывает сертификат сервера, используя свой закрытый ключ. Затем клиент может проверить, что сервер имеет сертификат ЦС, известный платформе.

Доверенные центры сертификации обычно указаны на хост-платформе. Android 8.0 (уровень API 26) включает более 100 центров сертификации, которые обновляются в каждой версии и не меняются на разных устройствах.

Клиентским приложениям необходим механизм проверки сервера, поскольку центр сертификации предлагает сертификаты для множества серверов. Сертификат ЦС идентифицирует сервер, используя либо определенное имя, например gmail.com , либо подстановочный знак, например *.google.com .

Чтобы просмотреть информацию о сертификате сервера веб-сайта, используйте команду s_client инструмента openssl , передав номер порта. По умолчанию HTTPS использует порт 443.

Команда передает выходные данные openssl s_client в openssl x509 , который форматирует информацию сертификата в стандарте X.509 . Команда запрашивает тему (имя сервера) и эмитента (CA).

openssl s_client -connect WEBSITE-URL:443 | \
  openssl x509 -noout -subject -issuer

Пример HTTPS

Предполагая, что у вас есть веб-сервер с сертификатом, выданным известным центром сертификации, вы можете сделать безопасный запрос, как показано в следующем коде:

Котлин

val url = URL("https://wikipedia.org")
val urlConnection: URLConnection = url.openConnection()
val inputStream: InputStream = urlConnection.getInputStream()
copyInputStreamToOutputStream(inputStream, System.out)

Ява

URL url = new URL("https://wikipedia.org");
URLConnection urlConnection = url.openConnection();
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);

Чтобы настроить HTTP-запросы, приведите их к HttpURLConnection . Документация Android HttpURLConnection включает примеры обработки заголовков запросов и ответов, публикации контента, управления файлами cookie, использования прокси, кэширования ответов и многого другого. Платформа Android проверяет сертификаты и имена хостов с помощью этих API.

Используйте эти API, когда это возможно. В следующем разделе рассматриваются распространенные проблемы, требующие различных решений.

Распространенные проблемы с проверкой сертификатов сервера

Предположим, что вместо возврата содержимого getInputStream() выдает исключение:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
        at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:374)
        at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:209)
        at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478)
        at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433)
        at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
        at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
        at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
        at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177)
        at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)

Это может произойти по нескольким причинам, в том числе:

  1. Центр сертификации, выдавший сертификат сервера, был неизвестен .
  2. Сертификат сервера не был подписан центром сертификации, а был самоподписанным .
  3. В конфигурации сервера отсутствует промежуточный центр сертификации .

В следующих разделах обсуждается, как решить эти проблемы, сохраняя при этом безопасность соединения с сервером.

Неизвестный центр сертификации

SSLHandshakeException возникает потому, что система не доверяет центру сертификации. Это может быть связано с тем, что у вас есть сертификат нового центра сертификации, которому Android не доверяет, или потому, что ваше приложение работает на более ранней версии без центра сертификации. Поскольку центр сертификации является частным, о нем редко знают. Чаще всего ЦС неизвестен, поскольку это не общедоступный ЦС, а скорее частный, выданный такой организацией, как правительство, корпорация или образовательное учреждение, для собственного использования.

Чтобы доверять пользовательским центрам сертификации без необходимости изменения кода вашего приложения, измените конфигурацию сетевой безопасности .

Внимание: на многих веб-сайтах описывается плохое альтернативное решение — установка TrustManager , который ничего не делает. Это делает ваших пользователей уязвимыми для атак при использовании общедоступной точки доступа Wi-Fi, поскольку злоумышленник может использовать уловки DNS для отправки трафика ваших пользователей через прокси-сервер, который выдает себя за ваш сервер. Злоумышленник может затем записать пароли и другие личные данные. Это работает, поскольку злоумышленник может сгенерировать сертификат, и без TrustManager , проверяющего, что сертификат получен из надежного источника, вы не сможете заблокировать этот тип атаки. Так что не делайте этого, даже временно. Вместо этого сделайте так, чтобы ваше приложение доверяло эмитенту сертификата сервера.

Самозаверяющий сертификат сервера

Во-вторых, SSLHandshakeException может возникнуть из-за самозаверяющего сертификата, что сделает сервер собственным центром сертификации. Это похоже на неизвестный центр сертификации, поэтому измените конфигурацию сетевой безопасности вашего приложения, чтобы доверять вашим самозаверяющим сертификатам.

Отсутствует промежуточный центр сертификации

В-третьих, SSLHandshakeException возникает из-за отсутствия промежуточного центра сертификации. Публичные центры сертификации редко подписывают сертификаты серверов. Вместо этого корневой центр сертификации подписывает промежуточные центры сертификации.

Чтобы снизить риск компрометации, центры сертификации держат корневой центр сертификации в автономном режиме. Однако операционные системы, такие как Android, обычно напрямую доверяют только корневым центрам сертификации, оставляя небольшой разрыв в доверии между сертификатом сервера, подписанным промежуточным центром сертификации, и верификатором сертификата, который распознает корневой центр сертификации.

Чтобы устранить этот разрыв в доверии, сервер отправляет цепочку сертификатов от ЦС сервера через любые промежуточные звенья в доверенный корневой ЦС во время установления связи TLS.

Например, вот цепочка сертификатов mail.google.com , просматриваемая командой openssl s_client :

$ openssl s_client -connect mail.google.com:443
---
Certificate chain
 0 s:/C=US/ST=California/L=Mountain View/O=Google LLC/CN=mail.google.com
   i:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
 1 s:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
   i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
---

Это показывает, что сервер отправляет сертификат для mail.google.com , выданный центром сертификации Thawte SGC , который является промежуточным центром сертификации, и второй сертификат для центра сертификации Thawte SGC , выданный центром сертификации Verisign , который является основным центром сертификации, которому доверяет Андроид.

Однако сервер может быть не настроен на включение необходимого промежуточного центра сертификации. Например, вот сервер, который может вызывать ошибку в браузерах Android и исключения в приложениях Android:

$ openssl s_client -connect egov.uscis.gov:443
---
Certificate chain
 0 s:/C=US/ST=District Of Columbia/L=Washington/O=U.S. Department of Homeland Security/OU=United States Citizenship and Immigration Services/OU=Terms of use at www.verisign.com/rpa (c)05/CN=egov.uscis.gov
   i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 International Server CA - G3
---

В отличие от неизвестного центра сертификации или самозаверяющего сертификата сервера, большинство настольных браузеров не выдают ошибок при взаимодействии с этим сервером. Браузеры настольных компьютеров кэшируют доверенные промежуточные центры сертификации. Узнав о промежуточном ЦС с одного сайта, браузеру он больше не понадобится в цепочке сертификатов.

Некоторые сайты намеренно делают это для вторичных веб-серверов, обслуживающих ресурсы. Чтобы сэкономить пропускную способность, они могут обслуживать свою главную HTML-страницу с сервера с полной цепочкой сертификатов, но свои изображения, CSS и JavaScript без ЦС. К сожалению, иногда эти серверы могут предоставлять веб-сервис, к которому вы пытаетесь обратиться из своего приложения Android, что не так толерантно.

Чтобы устранить эту проблему, настройте сервер так, чтобы он включал промежуточный центр сертификации в цепочку серверов. Большинство центров сертификации предоставляют инструкции о том, как это сделать для обычных веб-серверов.

Предупреждения об использовании SSLSocket напрямую

До сих пор примеры были сосредоточены на HTTPS с использованием HttpsURLConnection . Иногда приложениям необходимо использовать TLS отдельно от HTTPS. Например, приложение электронной почты может использовать варианты TLS SMTP, POP3 или IMAP. В этих случаях приложение может использовать SSLSocket напрямую, почти так же, как это делает HttpsURLConnection внутри себя.

Описанные до сих пор методы решения проблем с проверкой сертификатов также применимы к SSLSocket . Фактически, при использовании специального TrustManager в HttpsURLConnection передается SSLSocketFactory . Поэтому, если вам нужно использовать собственный TrustManager с SSLSocket , выполните те же действия и используйте SSLSocketFactory для создания SSLSocket .

Внимание: SSLSocket не выполняет проверку имени хоста. Ваше приложение должно выполнить собственную проверку имени хоста, предпочтительно путем вызова getDefaultHostnameVerifier() с ожидаемым именем хоста. Также имейте в виду, что HostnameVerifier.verify() не генерирует исключение в случае ошибки. Вместо этого он возвращает логический результат, который необходимо явно проверить.

Заблокированные центры сертификации

TLS использует центры сертификации для выдачи сертификатов только проверенным владельцам серверов и доменов. В редких случаях центры сертификации либо обманываются, либо, как в случае с Comodo или DigiNotar , взламываются, в результате чего сертификаты для имени хоста выдаются кому-то, кроме владельца сервера или домена.

Чтобы снизить этот риск, Android имеет возможность добавлять определенные сертификаты или даже целые центры сертификации в список запрещенных. Хотя этот список исторически был встроен в операционную систему, начиная с Android 4.2 этот список можно обновлять удаленно для предотвращения будущих угроз.

Ограничение вашего приложения определенными сертификатами

Внимание: закрепление сертификата (практика ограничения количества сертификатов, которые считаются действительными для вашего приложения, только теми, которые вы ранее авторизовали), не рекомендуется для приложений Android. Будущие изменения конфигурации сервера, такие как переход на другой центр сертификации, приводят к тому, что приложения с закрепленными сертификатами не могут подключиться к серверу без получения обновления клиентского программного обеспечения.

Если вы хотите, чтобы ваше приложение принимало только указанные вами сертификаты, очень важно включить несколько резервных контактов, включая хотя бы один ключ, который полностью находится под вашим контролем, и достаточно короткий срок действия, чтобы предотвратить проблемы совместимости. Конфигурация сетевой безопасности обеспечивает закрепление этих возможностей.

Клиентские сертификаты

В этой статье основное внимание уделяется использованию TLS для защиты связи с серверами. TLS также поддерживает концепцию клиентских сертификатов, которые позволяют серверу проверять личность клиента. Хотя используемые методы выходят за рамки этой статьи, они аналогичны указанию пользовательского TrustManager .

Nogotofail: инструмент тестирования безопасности сетевого трафика.

Nogotofail — это инструмент, который дает вам простой способ убедиться, что ваши приложения защищены от известных уязвимостей и неправильных конфигураций TLS/SSL. Это автоматизированный, мощный и масштабируемый инструмент для тестирования проблем сетевой безопасности на любом устройстве, сетевой трафик которого может проходить через него.

Nogotofail полезен в трех основных случаях использования:

  • Поиск ошибок и уязвимостей.
  • Проверка исправлений и наблюдение за регрессиями.
  • Понимание того, какие приложения и устройства какой трафик генерируют.

Nogotofail работает на Android, iOS, Linux, Windows, ChromeOS, macOS и фактически на любом устройстве, которое вы используете для подключения к Интернету. Доступен клиент для настройки параметров и получения уведомлений на Android и Linux, а сам механизм атаки можно развернуть в качестве маршрутизатора, VPN-сервера или прокси.

Вы можете получить доступ к инструменту в проекте с открытым исходным кодом Nogotofail .

Обновления SSL и TLS

Андроид 10

Некоторые браузеры, такие как Google Chrome, позволяют пользователям выбирать сертификат, когда сервер TLS отправляет сообщение с запросом сертификата в рамках рукопожатия TLS. Начиная с Android 10, объекты KeyChain учитывают эмитентов и параметры спецификации ключа при вызове KeyChain.choosePrivateKeyAlias() чтобы показать пользователям запрос на выбор сертификата. В частности, это приглашение не содержит вариантов, не соответствующих спецификациям сервера.

Если доступные сертификаты, выбираемые пользователем, отсутствуют, как в случае, когда ни один сертификат не соответствует спецификации сервера или на устройстве не установлены никакие сертификаты, запрос на выбор сертификата вообще не отображается.

Кроме того, на Android 10 или более поздней версии не требуется блокировка экрана устройства для импорта ключей или сертификатов CA в объект KeyChain.

TLS 1.3 включен по умолчанию

В Android 10 и более поздних версиях TLS 1.3 включен по умолчанию для всех TLS-соединений. Вот несколько важных подробностей о нашей реализации TLS 1.3:

  • Наборы шифров TLS 1.3 нельзя настроить. Поддерживаемые наборы шифров TLS 1.3 всегда включены, когда включен TLS 1.3. Любая попытка отключить их путем вызова setEnabledCipherSuites() игнорируется.
  • При согласовании TLS 1.3 объекты HandshakeCompletedListener вызываются перед добавлением сеансов в кэш сеансов. (В TLS 1.2 и других предыдущих версиях эти объекты вызываются после добавления сеансов в кэш сеансов.)
  • В некоторых ситуациях, когда экземпляры SSLEngine выдают исключение SSLHandshakeException в предыдущих версиях Android, эти экземпляры вместо этого выдают SSLProtocolException в Android 10 и более поздних версиях.
  • Режим 0-RTT не поддерживается.

При желании вы можете получить SSLContext с отключенным TLS 1.3, вызвав SSLContext.getInstance("TLSv1.2") . Вы также можете включать или отключать версии протокола для каждого соединения, вызывая setEnabledProtocols() для соответствующего объекта.

Сертификатам, подписанным с помощью SHA-1, не доверяют TLS.

В Android 10 сертификатам, использующим алгоритм хеширования SHA-1, не доверяют соединения TLS. Корневые центры сертификации не выдавали такие сертификаты с 2016 года, и им больше не доверяют Chrome и другие основные браузеры.

Любая попытка подключения завершается неудачей, если соединение осуществляется с сайтом, который предоставляет сертификат с использованием SHA-1.

Изменения и улучшения поведения KeyChain

Некоторые браузеры, такие как Google Chrome, позволяют пользователям выбирать сертификат, когда сервер TLS отправляет сообщение с запросом сертификата в рамках рукопожатия TLS. Начиная с Android 10, объекты KeyChain учитывают эмитентов и параметры спецификации ключа при вызове KeyChain.choosePrivateKeyAlias() чтобы показать пользователям запрос на выбор сертификата. В частности, это приглашение не содержит вариантов, не соответствующих спецификациям сервера.

Если доступные сертификаты, выбираемые пользователем, отсутствуют, как в случае, когда ни один сертификат не соответствует спецификации сервера или на устройстве не установлены никакие сертификаты, запрос на выбор сертификата вообще не появляется.

Кроме того, на Android 10 или более поздней версии не требуется блокировка экрана устройства для импорта ключей или сертификатов CA в объект KeyChain.

Другие изменения TLS и криптографии

В библиотеках TLS и криптографии произошло несколько незначительных изменений, которые вступят в силу в Android 10:

  • Шифры AES/GCM/NoPadding и ChaCha20/Poly1305/NoPadding возвращают более точные размеры буфера из getOutputSize() .
  • Набор шифров TLS_FALLBACK_SCSV исключается из попыток подключения с максимальным протоколом TLS 1,2 или выше. Из-за улучшений в реализации сервера TLS мы не рекомендуем пытаться использовать внешний резерв TLS. Вместо этого мы рекомендуем полагаться на согласование версии TLS.
  • ChaCha20-Poly1305 — это псевдоним ChaCha20/Poly1305/NoPadding.
  • Имена хостов с точками в конце не считаются действительными именами хостов SNI.
  • Расширение support_signature_algorithms в CertificateRequest учитывается при выборе ключа подписи для ответов сертификата.
  • Непрозрачные ключи подписи, например ключи из Android Keystore, можно использовать с подписями RSA-PSS в TLS.

Изменения HTTPS-соединения

Если приложение под управлением Android 10 передает значение null в setSSLSocketFactory() , возникает IllegalArgumentException . В предыдущих версиях передача null в setSSLSocketFactory() имела тот же эффект, что и передача текущей фабрики по умолчанию .

Андроид 11

SSL-сокеты по умолчанию используют механизм Conscrypt SSL.

Реализация SSLSocket в Android по умолчанию основана на Conscrypt . Начиная с Android 11, эта реализация построена на основе SSLEngine от Conscrypt.