Зашифрованные взаимодействия между клиентом и сервером используют протокол Transport Layer Security (TLS) для защиты данных вашего приложения.
В этой статье обсуждаются лучшие практики, связанные с лучшими практиками защищенного сетевого протокола и соображениями инфраструктуры открытого ключа (PKI) . Прочтите Обзор безопасности Android , а также Обзор разрешений для получения более подробной информации.
Концепции
Сервер с сертификатом TLS имеет открытый ключ и соответствующий закрытый ключ. Сервер использует криптографию с открытым ключом для подписи своего сертификата во время рукопожатия TLS.
Простое рукопожатие только подтверждает, что сервер знает закрытый ключ сертификата. Чтобы решить эту ситуацию, позвольте клиенту доверять нескольким сертификатам. Данный сервер не заслуживает доверия, если его сертификат не отображается в наборе доверенных сертификатов на стороне клиента.
Однако серверы могут использовать ротацию ключей для замены открытого ключа своего сертификата на новый. Изменение конфигурации сервера требует обновления клиентского приложения. Если сервер является сторонней веб-службой, например веб-браузером или приложением электронной почты, сложнее узнать, когда следует обновить клиентское приложение.
Серверы обычно полагаются на сертификаты центров сертификации (CA) для выдачи сертификатов, что сохраняет конфигурацию на стороне клиента более стабильной с течением времени. CA подписывает сертификат сервера, используя свой закрытый ключ. Затем клиент может проверить, что у сервера есть сертификат CA, известный платформе.
Доверенные центры сертификации обычно перечислены на хост-платформе. Android 8.0 (уровень API 26) включает более 100 центров сертификации, которые обновляются в каждой версии и не меняются между устройствами.
Клиентским приложениям нужен механизм для проверки сервера, поскольку CA предлагает сертификаты для многочисленных серверов. Сертификат CA идентифицирует сервер, используя либо определенное имя, например 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)
Это может произойти по нескольким причинам, включая:
- Центр сертификации, выдавший сертификат сервера, неизвестен .
- Сертификат сервера не был подписан центром сертификации, а был самоподписанным .
- В конфигурации сервера отсутствует промежуточный центр сертификации .
В следующих разделах обсуждается, как решить эти проблемы, сохранив при этом безопасность подключения к серверу.
Неизвестный центр сертификации
SSLHandshakeException
возникает, потому что система не доверяет CA. Это может быть связано с тем, что у вас есть сертификат от нового CA, которому Android не доверяет, или с тем, что ваше приложение работает на более ранней версии без CA. Поскольку CA является частным, он редко известен. Чаще CA неизвестен, потому что он не является публичным CA, а скорее частным, выпущенным организацией, такой как правительство, корпорация или образовательное учреждение для собственного использования.
Чтобы доверять пользовательским центрам сертификации без необходимости изменения кода приложения, измените конфигурацию сетевой безопасности .
Внимание: многие веб-сайты описывают плохое альтернативное решение, которое заключается в установке TrustManager
, который ничего не делает. Это делает ваших пользователей уязвимыми для атак при использовании публичной точки доступа Wi-Fi, поскольку злоумышленник может использовать DNS-трюки для отправки трафика ваших пользователей через прокси-сервер, который выдает себя за ваш сервер. Затем злоумышленник может записывать пароли и другие личные данные. Это работает, поскольку злоумышленник может сгенерировать сертификат, и без проверки TrustManager
того, что сертификат исходит из надежного источника, вы не сможете заблокировать этот тип атаки. Так что не делайте этого, даже временно. Вместо этого сделайте так, чтобы ваше приложение доверяло издателю сертификата сервера.
Самоподписанный сертификат сервера
Во-вторых, SSLHandshakeException
может возникнуть из-за самоподписанного сертификата, что делает сервер своим собственным CA. Это похоже на неизвестный центр сертификации, поэтому измените конфигурацию сетевой безопасности вашего приложения, чтобы доверять вашим самоподписанным сертификатам.
Отсутствует промежуточный центр сертификации
В-третьих, SSLHandshakeException
возникает из-за отсутствия промежуточного CA. Публичные CA редко подписывают серверные сертификаты. Вместо этого корневой CA подписывает промежуточные CA.
Чтобы снизить риск компрометации, центры сертификации держат корневой центр сертификации в автономном режиме. Однако операционные системы, такие как Android, обычно доверяют только корневым центрам сертификации напрямую, оставляя небольшой промежуток доверия между сертификатом сервера, подписанным промежуточным центром сертификации, и верификатором сертификатов, который распознает корневой центр сертификации.
Чтобы устранить этот пробел в доверии, сервер отправляет цепочку сертификатов от сервера CA через всех посредников доверенному корневому CA во время установления связи 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 CA, который является промежуточным CA, и второй сертификат для Thawte SGC CA, выданный Verisign CA, который является основным CA, которому доверяет Android.
Однако сервер может быть не настроен на включение необходимого промежуточного CA. Например, вот сервер, который может вызывать ошибку в браузерах 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 ---
В отличие от неизвестного CA или самоподписанного сертификата сервера, большинство настольных браузеров не выдают ошибку при взаимодействии с этим сервером. Настольные браузеры кэшируют доверенные промежуточные CA. Узнав о промежуточном CA с одного сайта, браузеру он больше не понадобится в цепочке сертификатов.
Некоторые сайты намеренно делают это для вторичных веб-серверов, обслуживающих ресурсы. Чтобы сэкономить пропускную способность, они могут обслуживать свою главную HTML-страницу с сервера с полной цепочкой сертификатов, но их изображения, CSS и JavaScript без CA. К сожалению, иногда эти серверы могут предоставлять веб-сервис, к которому вы пытаетесь обратиться из своего приложения Android, что не так толерантно.
Чтобы исправить эту проблему, настройте сервер так, чтобы включить промежуточный CA в цепочку серверов. Большинство CA предоставляют инструкции о том, как это сделать для обычных веб-серверов.
Предупреждения об использовании SSLSocket напрямую
До сих пор примеры были сосредоточены на HTTPS с использованием HttpsURLConnection
. Иногда приложениям необходимо использовать TLS отдельно от HTTPS. Например, приложение электронной почты может использовать варианты TLS SMTP, POP3 или IMAP. В этих случаях приложение может использовать SSLSocket
напрямую, во многом так же, как это делает HttpsURLConnection
внутри.
Описанные до сих пор методы решения проблем проверки сертификатов также применимы к SSLSocket
. Фактически, при использовании пользовательского TrustManager
, то, что передается в HttpsURLConnection
— это SSLSocketFactory
. Поэтому, если вам нужно использовать пользовательский TrustManager
с SSLSocket
, выполните те же шаги и используйте этот SSLSocketFactory
для создания вашего SSLSocket
.
Внимание: SSLSocket
не выполняет проверку имени хоста. Ваше приложение должно выполнить собственную проверку имени хоста, желательно вызвав getDefaultHostnameVerifier()
с ожидаемым именем хоста. Также имейте в виду, что HostnameVerifier.verify()
не выдает исключение при ошибке. Вместо этого он возвращает логический результат, который вы должны явно проверить.
Проверка сертификата
TLS полагается на CA, чтобы выдавать сертификаты только проверенным владельцам серверов и доменов. В редких случаях CA либо обманываются, либо, как в случае Comodo или DigiNotar , взламываются, в результате чего сертификаты для имени хоста выдаются кому-то другому, а не владельцу сервера или домена.
Чтобы снизить этот риск, Android обрабатывает отзыв сертификатов в масштабах всей системы, используя комбинацию черного списка и прозрачности сертификатов, не полагаясь на онлайн-проверку сертификатов. Кроме того, Android будет проверять ответы OCSP, прикрепленные к рукопожатию TLS.
Чтобы включить прозрачность сертификатов в вашем приложении, см. раздел «Включение прозрачности сертификатов» в нашей документации по настройке сетевой безопасности.
Ограничение вашего приложения определенными сертификатами
Внимание: закрепление сертификатов, практика ограничения сертификатов, которые считаются действительными для вашего приложения, теми, которые вы ранее авторизовали, не рекомендуется для приложений Android. Будущие изменения конфигурации сервера, такие как смена на другой CA, делают приложения с закрепленными сертификатами неспособными подключаться к серверу без получения обновления клиентского программного обеспечения.
Если вы хотите ограничить свое приложение, чтобы оно принимало только указанные вами сертификаты, крайне важно включить несколько резервных пинов, включая по крайней мере один ключ, который полностью находится под вашим контролем, и достаточно короткий срок действия, чтобы предотвратить проблемы совместимости. Конфигурация сетевой безопасности предоставляет пиннинг с этими возможностями.
Сертификаты клиентов
В этой статье основное внимание уделяется использованию 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.
- Расширение supported_signature_algorithms в CertificateRequest учитывается при выборе ключа подписи для ответов сертификатов.
- С подписями RSA-PSS в TLS можно использовать непрозрачные ключи подписи, например из Android Keystore.
Изменения HTTPS-соединения
Если приложение под управлением Android 10 передает null в setSSLSocketFactory()
, возникает IllegalArgumentException
. В предыдущих версиях передача null в setSSLSocketFactory()
имела тот же эффект, что и передача в текущей default factory .
Андроид 11
Сокеты SSL по умолчанию используют механизм Conscrypt SSL.
Реализация SSLSocket в Android по умолчанию основана на Conscrypt
. Начиная с Android 11, эта реализация внутренне построена поверх SSLEngine от Conscrypt.