Как стать автором
Обновить
80.36
Swordfish Security
Информационная безопасность, DevSecOps, SSDL

И снова про App Transport Security: что это и зачем

Время на прочтение20 мин
Количество просмотров7.3K

Привет, Хабр!

Меня зовут Юрий Шабалин, и, как я пишу в начале каждой своей статьи, мы разрабатываем платформу анализа защищенности мобильных приложений iOS и Android.

В этой статье мне бы хотелось затронуть тему безопасной конфигурации сетевого взаимодействия, а также немного расширить предыдущую статью по SSL Pinning для механизма защиты канала связи в iOS. А именно, я расскажу про App Transport Security: для чего он нужен, использовать ли его или отключать в приложениях, в чем его польза.

На эту тему существуют статьи на англоязычных ресурсах (здесь можно многие из них найти в разделе “Ссылки“), достаточно подробно эта тема раскрыта в документации Apple, но на русском я статей практически не встречал. Но даже если они есть, я с удовольствием добавлю свой материал с практическими примерами в эту небольшую копилку.

Оглавление

Введение

Apple от версии к версии iOS продолжает радовать нас различными нововведениями в части безопасности. В последнее время это больше связано с приватностью пользователей, их данными, местоположением и т.д. А для нас особый интерес представляет обновление механизма конфигурации безопасного сетевого соединения - App Transport Security - в iOS 9.0 в 2015 году.

Это событие можно рассматривать как попытку Apple сделать мобильные приложения более защищенными, точнее, дать разработчикам инструмент для самостоятельного управления настройками сетевого соединения, и не через код приложения, а посредством конфигурации специального домена в основном файле Info.plist. Ключевыми задачами данного механизма являются отключение для приложения возможности общаться по незащищенному протоколу HTTP, а также обязательная поддержка последних версий TLS на сервере для обеспечения Perfect Forward Secrecy (про это можно подробно почитать в нашей статье про SSL Pinning). Изначально компания Apple собиралась сделать применение App Transport Security обязательным для всех приложений, представленных в App Store, начиная с января 2017 года. Однако, за несколько недель до этой даты решение изменили. И, хотя было анонсировано назначение новой даты, до сих пор использование ATS является необязательным.

Нечто подобное предложили в дальнейшем и в Android 7 в 2016 году, в виде файла настройки Network Security Config.

Что интересно, различные рекламные сервисы, например, Google AdMob, для своей корректной работы рекомендуют просто полностью отключить ATS в вашем приложении. Классный совет, спасибо, Google! Мы всегда знали, что ты - за безопасность!

Проверка сервера

Не всякий сервер подойдет для подключения с использованием ATS. Если ваш не удовлетворяет необходимым требованиям, то соединение завершится с ошибкой
An SSL error has occurred and a secure connection to the server cannot be made.

Вот каким должен быть сервер, чтобы применять ATS:

  • Все шифронаборы должны использовать алгоритмы, поддерживающие Perfect Forward Secrecy;

  • Наличие TLS версии не ниже 1.2;

  • Для всех сертификатов необходимо использовать, как минимум, отпечаток SHA256 с ключом RSA (2048 или выше) или с ключом 256 бит или более.

ATS-совместимые шифры

Для успешного подключения к серверу с использованием ATS на клиенте подойдут следующие шифронаборы:

  • TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384

  • TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256

  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384

  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA

  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256

  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA

  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384

  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256

  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA

Тестирование серверной части

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

Для того, чтобы определить, какие настройки необходимо проделать с ATS, чтобы он корректно работал, можно использовать команду nscurl с параметром --ats-diagnostics. Результатом вывода команды будут успешные и неуспешные проверки по всем пунктам ATS:

$ nscurl --ats-diagnostics https://stingray-mobile.ru/
Starting ATS Diagnostics

<...>

Default ATS Secure Connection
---
ATS Default Connection
Result : PASS
---

================================================================================

Allowing Arbitrary Loads

---
Allow All Loads
Result : PASS
---

================================================================================

Configuring TLS exceptions for stingray-mobile.ru

---
TLSv1.3
Result : PASS
---

---
TLSv1.2
Result : PASS
---

---
TLSv1.1
Result : PASS
---

---
TLSv1.0
Result : PASS
---

<...>

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

К примеру, если не отработала проверка на Perfect Forward Secrecy:

$ nscurl --ats-diagnostics http://test.com
<...>
Default ATS Secure Connection
---
ATS Default Connection
2015-08-28 11:51:06.868 nscurl[7019:8960694] NSURLSession/
NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
Result : FAIL
---
<...>
Configuring PFS exceptions for test.com
---
Disabling Perfect Forward Secrecy
Result : PASS
---
<...>

В этом примере видно, что успешно установить соединение будет возможно, только если отключить Perfect Forward Secrecy. Поэтому в Info.plist файле для домена test.com необходимо указать NSExceptionRequiresForwardSecrecy=NO.

Для того, чтобы проверить, какие шифронаборы используются и включены на вашем сервере, можно запустить nmap со специальными параметрами

$ nmap --script ssl-enum-ciphers -p 443 www.cnn.com

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

$ nmap --script ssl-enum-ciphers -p 443 www.cnn.com

Starting Nmap 7.91 ( https://nmap.org ) at 2022-03-14 15:43 MSK
Nmap scan report for www.cnn.com (151.101.65.67)
Host is up (0.040s latency).
Other addresses for www.cnn.com (not scanned): 151.101.129.67 151.101.193.67 151.101.1.67

PORT    STATE SERVICE
443/tcp open  https
| ssl-enum-ciphers:
|   TLSv1.0:
|     ciphers:
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
|     compressors:
|       NULL
|     cipher preference: server
|     warnings:
|       64-bit block cipher 3DES vulnerable to SWEET32 attack
|   TLSv1.1:
|     ciphers:
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
|     compressors:
|       NULL
|     cipher preference: server
|     warnings:
|       64-bit block cipher 3DES vulnerable to SWEET32 attack
|   TLSv1.2:
|     ciphers:
|       TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A
|       TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
|     compressors:
|       NULL
|     cipher preference: server
|     warnings:
|       64-bit block cipher 3DES vulnerable to SWEET32 attack
|_  least strength: C

Nmap done: 1 IP address (1 host up) scanned in 11.38 seconds

Кроме того, существуют сайты, например, http://ssllabs.com/ssltest/analyze.html, позволяющие проверить, совместимы ли общедоступные веб-сайты с ATS.

App Transport Security

Секция по настройке App Transport Security в Info.plist называется NSAppTransportSecurity и имеет следующую структуру:

NSAppTransportSecurity : Dictionary {
    NSAllowsArbitraryLoads : Boolean
    NSAllowsArbitraryLoadsForMedia : Boolean
    NSAllowsArbitraryLoadsInWebContent : Boolean
    NSAllowsLocalNetworking : Boolean
    NSExceptionDomains : Dictionary {
        <domain-name-string> : Dictionary {
            NSIncludesSubdomains : Boolean
            NSExceptionAllowsInsecureHTTPLoads : Boolean
            NSExceptionMinimumTLSVersion : String
            NSExceptionRequiresForwardSecrecy : Boolean   
            NSRequiresCertificateTransparency : Boolean
        }
    }
}

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

NSAllowsArbitraryLoads

Ключ NSAllowsArbitraryLoads определяет состояние ATS в целом, видит включено или выключено оно для приложения. По умолчанию, для ключа NSAllowsArbitraryLoads установлено значение NO, то есть App Transport Security включен полностью. Установка ключа в значение YESприведет к полному отключению всех проверок. Это означает, что система не будет запрещать приложению взаимодействовать с любыми доменами по протоколу HTTP, а также не будет применять никаких дополнительных проверок к установке сетевого соединения. Крайне не рекомендуется отключать ATS на уровне всего приложения. Лучше потратить немного времени и разобраться, что не работает и как это можно исправить.

Если вы все-таки решите отключить ATS, то рекомендую проконтролировать несколько моментов, которые автоматически проверяются при его использовании:

  • Шифры для сетевого взаимодействия приложения (и их надежность);

  • Протоколы, применяемые для отправки и получения данных (и их безопасность);

  • Наличие в приложении уязвимостей для перехода на более раннюю версию протокола шифрования;

  • Осуществление проверок приложения сертификатами, используемыми для TLS-подключений.

Как верно заметили в комментариях, поведение этого ключа зависит от используемой версии iOS:

  • iOS 9.0
    ATS использует заданное значение NSAllowsArbitraryLoads (или NO по умолчанию) и игнорирует другие глобальные исключения.

  • iOS 10.0 и более старшие версии
    ATS игнорирует установленное значение NSAllowsArbitraryLoads, если какой-либо из следующих ключей присутствует в конфигурации:

    • NSAllowsArbitraryLoadsForMedia

    • NSAllowsArbitraryLoadsInWebContent

    • NSAllowsLocalNetworking

NSAllowsLoadsForMedia

Это исключение относится к мультимедийному контенту, защищенному системой управления цифровыми правами (DRM) или шифрованием. По умолчанию, для ключа NSAllowsLoadsForMedia установлено значение NO. Если для него установлено значение YES, ATS отключается для контента, отправляемого с использованием фреймворка AVFoundation. Обычно это происходит с приложениями, включающими в себя возможность работы с видео/аудио контентом. 

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

По опыту: я не очень часто встречал подобные исключения и подобный контент в приложениях. Уверен, что они есть, но в моей практике они достаточно редки.

NSAllowsArbitraryLoadsInWebContent

Есть один интересный нюанс: если WebView (а именно, компоненты UIWebView и WKWebView) используется для отображения произвольных адресов, то невозможно составить весь их список и понять, правильно ли они настроены для соединения при помощи ATS. Apple предоставила два возможных решения данной проблемы.

Первое из них - это использование ключа NSAllowsArbitraryLoadsInWebContent. Он определяет, возможно ли соединение по небезопасным протоколам из компонентов WebView. По умолчанию, для этого ключа установлено значение NO. Если установить значение YES, ATS будет отключен для WebView. Этот ключ появился только в десятой версии iOS. Есть несколько нюансов, относящихся к старым версиям iOS, но их мы рассматривать не будем, поскольку они уже неактуальны.

Второй, и более адекватный вариант, - использование SFSafariViewController, который был специально разработан для этих целей. Обратимся к документации Apple:

“Используйте класс SFSafariViewController, если ваше приложение позволяет пользователям открывать произвольные Web-сайты. Используйте класс WKWebView для контента, который находится под вашим управлением”.

Применяя SFSafariViewController, нужно помнить о некоторых специфических вещах:

  • ATS в данном компоненте отключен. Это позволяет загружать и отображать любой веб-контент с любых источников независимо от конфигурации HTTPS;

  • Cookies и данные Web-сайтов передаются Safari. Это позволяет, например, оставаться аутентифицированным на различных ресурсах, в которых пользователь логинился из браузера;

  • Способов контроля и управления по сравнению с WKWebView существенно меньше.

Из этих двух вариантов, при возможности, лучше использовать компонент SFSafariViewController для отображения произвольных веб-сайтов вместо отключения ATS для всех WebView.

NSAllowsLocalNetworking

Ключ NSAllowsLocalNetworking определяет работу ATS в локальной сети.

По умолчанию, для NSAllowsLocalNetworkingустановлено значение NO.  Обычно это исключение используют приложения, подключающиеся к локальным устройствам для работы Интернета вещей (IoT). При отключении ATS убедитесь, что во время взаимодействия в локальной сети не передаются конфиденциальные данные, а также используется безопасное TLS-соединение.

NSExceptionDomains

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

Вторая особенность заключается в том, что если домен в исключениях указан без какой-либо конфигурации, то он получит полную защиту ATS, даже если для параметра NSAllowsArbitraryLoadsустановлено значение YES. Таким образом, разработчик может отключить ATS глобально, но включить его для определенных доменов, указав их в ключе NSExceptionDomains. Это еще один способ позволить приложению осуществлять загрузку данных с произвольных серверов без их проверки на совместимость с ATS (при включенном ATS для выбранных доменов).

Но, на самом деле, это достаточно плохая практика и, возможно, стоит еще раз посмотреть на архитектуру вашего приложения.

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

NSIncludesSubdomains

Данный ключ определяет, будет ли применяться политика ATS для поддоменов.

По умолчанию, для ключа NSIncludesSubdomains установлено значение NO. Если установлено значение YES, любая конфигурация ATS, включенная для определенного домена, будет использоваться для всех поддоменов. И, если установлен домен, но не настроено никаких дополнительных ключей, кроме NSIncludesSubdomains, этот домен и его поддомены будут использовать ATS. 

NSExceptionAllowsInsecureHTTPLoads

Ключ NSExceptionAllowsInsecureHTTPLoadопределяет, возможна ли передача незащищенного трафика HTTP на указанный домен.

По умолчанию, для этого ключа установлено значение NO. Если установлено значение YES, приложению будет разрешено отправлять HTTP-трафик на этот домен. 

NSExceptionMinimumTLSVersion

Этот ключ позволяет снизить минимально допустимую версию TLS. По умолчанию, к таким версиям принадлежат TLS 1.2 и выше. 

NSExceptionRequiresForwardSecrecy

Данный ключ определяет использование свойства Forward Secrecy для конкретного домена.

По умолчанию, для этого ключа установлено значение YES. Если выставлено значение NO, свойство Forward Secrecy будет отключено для этого домена. 

NSRequiresCertificateTransparency

Данный ключ определяет использование свойства Certificate Transparency (прозрачность сертификатов) для конкретного домена.

По умолчанию, для этого ключа установлено значение NO. Если для ключа установлено значение YES, для сертификата домена потребуется метка времени Certificate Transparency.

Certificate Transparency - это проект Google, ориентированный на повышение безопасности системы выпуска сертификатов SSL. Если ваша организация или рассматриваемый домен поддерживает Certificate Transparency, рекомендуется включить данную опцию. Это помогает выявлять мошеннические центры сертификации и предотвращать атаки типа «человек посередине», уведомляя владельца, если его сертификат был скомпрометирован. Когда этот ключ включен, проверки сертификатов, связанные с Certificate Transparency, выполняются до установления соединения.

Certificate Pinning

Начиная с iOS 14, в AppTransport Security появился нативный механизм реализации прикрепления сертификатов (certificate pinning). В документации о нем сказано не много, основным источником информации является новость на портале. Резюмируем ее содержание:

  • В Info.plist можно указать набор сертификатов, которые App Transport Security (ATS) ожидает при подключении к указанным доменам

  • Закрепленный открытый ключ должен присутствовать либо в промежуточном, либо в корневом сертификате

  • Приложение не сможет подключиться к указанному в настройках домену, если проверка цепочки сертификатов окажется неуспешной

  • Можно связать несколько открытых ключей с одним доменным именем.

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

# servername is used to ensure SNI is requested
/usr/local/opt/openssl/bin/openssl s_client -showcerts -verify 5 -servername www.example.com -connect www.example.com:443 < /dev/null

# Output
verify depth is 5
CONNECTED(00000005)
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
verify return:1
depth=0 C = US, ST = California, L = Los Angeles, O = Internet Corporation for Assigned Names and Numbers, CN = www.example.org
verify return:1
---
Certificate chain
 0 s:C = US, ST = California, L = Los Angeles, O = Internet Corporation for Assigned Names and Numbers, CN = www.example.org
   i:C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
-----BEGIN CERTIFICATE-----
MIIG1TCCBb2gAwIBAgIQD74IsIVNBXOKsMzhya/uyTANBgkqhkiG9w0BAQsFADBP
<...>
vUzLnF7QYsJhvYtaYrZ2MLxGD+NFI8BkXw==
-----END CERTIFICATE-----
 1 s:C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
   i:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
-----BEGIN CERTIFICATE-----
MIIE6jCCA9KgAwIBAgIQCjUI1VwpKwF9+K1lwA/35DANBgkqhkiG9w0BAQsFADBh
<...>
8ks5T1KESaZMkE4f97Q=
-----END CERTIFICATE-----
 2 s:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
   i:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
<...>
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
---
Server certificate
subject=C = US, ST = California, L = Los Angeles, O = Internet Corporation for Assigned Names and Numbers, CN = www.example.org

issuer=C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 4654 bytes and written 747 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
DONE

Из вывода команды можно определить, что для http://www.example.com есть один конечный сертификат, сертификат промежуточного центра сертификации, а затем корневой сертификат.

Для получения отпечатка SPKI, можно сохранить любой сертификат в виде файла формата PEM, а затем передать его на вход в следующий скрипт (пример для MacOS):

#!/usr/bin/env bash
set -Eeuo pipefail

# Homebrew location of OpenSSL (built-in version is too out of date)
OPENSSL="/usr/local/opt/openssl/bin/openssl"
CERTHASH=$($OPENSSL x509 -inform pem -noout -outform pem -pubkey < "$1" | $OPENSSL pkey -pubin -inform pem -outform der | $OPENSSL dgst -sha256 -binary | $OPENSSL enc -base64)
echo -e "\nFingerprint for pinning: $CERTHASH"

Или же воспользоваться командой из документации:

$ cat ca.pem | openssl x509 -inform pem -noout -outform pem -pubkey | openssl pkey -pubin -inform pem -outform der | openssl dgst -sha256 -binary | openssl enc -base64

Результирующий отпечаток конечного сертификата в приведенном выше примере — mM294xslEgmvDODAxWWH2DeH4/bNgPBpgZvd7SfciuA=, а центр сертификации — RQeZkB42znUfsDIIFWIRiYEcKl7nHwNFwWCrnMMJbVc=.

Теперь мы можем добавить эти значения в Info.plist:

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSPinnedDomains</key>
  <dict>
    <key>example.com</key>
    <dict>
      <key>NSIncludesSubdomains</key>
      <true/>
      <key>NSPinnedCAIdentities</key>
      <array>
        <dict>
          <key>SPKI-SHA256-BASE64</key>
          <string>RQeZkB42znUfsDIIFWIRiYEcKl7nHwNFwWCrnMMJbVc=</string>
        </dict>
      </array>
      <key>NSPinnedLeafIdentities</key>
      <array>
        <dict>
          <key>SPKI-SHA256-BASE64</key>
          <string>mM294xslEgmvDODAxWWH2DeH4/bNgPBpgZvd7SfciuA=</string>
        </dict>
      </array>
    </dict>
  </dict>
</dict>

В приведенном выше примере мы указываем, что отпечаток открытого ключа связан с доменом example.org и его поддоменами, например test.example.org. Но вот поддомены третьего уровня и выше в эту проверку уже не попадают (например notinclude.test.example.org). Также будем проверять центр сертификации, что указано в ключе NSPinnedCAIdentities и сертификат конечного сервера, за который отвечает ключ NSPinnedLeafIdentities.

Можно сразу же указать несколько отпечатков, что может быть полезно при ротации сертификатов на сервере:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSPinnedDomains</key>
    <dict>
        <key>example.com</key>
        <dict>
            <key>NSPinnedLeafIdentities</key>
            <array>
                <dict>
                    <key>SPKI-SHA256-BASE64</key>
                    <string>i9HaIScvf6T/skE3/A7QOq5n5cTYs8UHNOEFCnkguSI=</string>
                </dict>
                <dict>
                    <key>SPKI-SHA256-BASE64</key>
                    <string>mM294xslEgmvDODAxWWH2DeH4/bNgPBpgZvd7SfciuA=</string>
                </dict>
            </array>
        </dict>
    </dict>
</dict>

Но как показывает практика, это работает не всегда. Такой способ хорош для URLSession, но не подходит для API поверх CFNetwork.

  • WKWebView по-прежнему будет подключаться и загружать контент из домена, если открытый ключ SSL отличается от указанного в Info.plist

  • SFSafariViewController также не учитывает настройки в Info.plist. Такое поведение неудивительно, учитывая, что SFSafariViewController выполняется в отдельном процессе.

Ответ Apple пока неутешительный, поскольку не несет в себе никакой конкретики. Непонятно, будут ли когда-то настройки Certificate Pinning из AppTransport Security работать для всех API. Может быть, однажды это случится, ну а сейчас просто надо учитывать эту особенность при разработке.

Идеальная конфигурация

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

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <false/>
    <key>NSAllowsArbitraryLoadsForMedia</key>
    <false/>
    <key>NSAllowsArbitraryLoadsInWebContent</key>
    <false/>
    <key>NSPinnedDomains</key>
    <dict>
        <key>example.com</key>
        <dict>
            <key>NSPinnedLeafIdentities</key>
            <array>
                <dict>
                    <key>SPKI-SHA256-BASE64</key>
                    <string>i9HaIScvf6T/skE3/A7QOq5n5cTYs8UHNOEFCnkguSI=</string>
                </dict>
                <dict>
                    <key>SPKI-SHA256-BASE64</key>
                    <string>mM294xslEgmvDODAxWWH2DeH4/bNgPBpgZvd7SfciuA=</string>
                </dict>
            </array>
        </dict>
    </dict>
</dict>

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

Примеры

Отключение ATS


Полностью отключить ATS можно, указав флагNSAllowsArbitraryLoads=YES. Такая конфигурация рекомендуется только для отладки.


	<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSAllowsArbitraryLoads</key>
		<true/>
	</dict>

К сожалению, во многих приложениях мы до сих пор встречаем полностью отключенный ATS:

Выявленная неправильная конфигурация App Transport Security
Выявленная неправильная конфигурация App Transport Security

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

Работа с исключениями

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

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

Срабатывание с адресом тестового стенда в конфигурации App Transport Security
Срабатывание с адресом тестового стенда в конфигурации App Transport Security

Как правило, такие стенды, если они имеют доступ в интернет, намного слабее “охраняются“ различными средствами защиты, мониторингом и прочими разработками. А значит, их интересно исследовать на предмет наличия уязвимостей. А дальше уже можно попробовать зайти в сеть и развернуться по-настоящему. Как я говорил в других статьях, одна уязвимость или даже недостаток в мобильном приложении не являются такой уж огромной проблемой (конечно, если это не какая-то инъекция или RCE). Однако, наличие нескольких слабостей помогает выстроить в вектор атаки, который сработает и принесет реальный ущерб приложению или пользователям.

Отключение ATS для всех соединений кроме одного

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

<?xml version=»1.0″ encoding=»UTF-8″?>
<!DOCTYPE plist PUBLIC «-//Apple//DTD PLIST 1.0//EN» «http://www.apple.com/DTDs/PropertyList-1.0.dtd»>
<plist version=»1.0″>
<dict>
    …
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
        <key>NSExceptionDomains</key>
        <dict>
            <key>api.test.com</key>
            <dict>
                <key>NSExceptionAllowsInsecureHTTPLoads</key>
                <false/>
            </dict>
        </dict>
    </dict>
    …
</dict>
</plist>

В этом случае приложение сможет осуществлять соединения со всеми серверами вне зависимости от корректности настройки HTTPS (да и просто по HTTP), но для выделенного в исключения домена api.test.comподключения можно будет осуществлять только по HTTPS. Также для него будут применяться все остальные требования ATS.

Отключение PFS для всех поддоменов

	<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSExceptionDomains</key>
		<dict>
			<key>test.com</key>
			<dict>
				<key>NSIncludesSubdomains</key>
				<true/>
				<key>NSExceptionRequiresForwardSecrecy</key>
				<false/>
			</dict>
		</dict>
	</dict>

При такой конфигурации подключение к домену test.com и всех его поддоменов не будет требовать настройки Perfect Forward Secrecy. Она может применяться, например, если на вашем сервере не настроены правильные шифронаборы, позволяющие использовать это крайне полезное свойство.

Совмещение исключений

В следующем Info.plist мы определим три исключения и затем разберем их более детально:

<?xml version=»1.0″ encoding=»UTF-8″?>
<!DOCTYPE plist PUBLIC «-//Apple//DTD PLIST 1.0//EN» «http://www.apple.com/DTDs/PropertyList-1.0.dtd»>
<plist version=»1.0″>
<dict>
    …
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>api.insecuredomain.com</key>
            <dict>
                <key>NSExceptionAllowsInsecureHTTPLoads</key>
                <false/>
            </dict>
            <key>cdn.somedomain.com</key>
            <dict>
                <key>NSThirdPartyExceptionMinimumTLSVersion</key>
                <string>1.1<string/>
            </dict>
            <key>otherdomain.com</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSExceptionRequiresForwardSecrecy</key>
                <false/>
            </dict>
        </dict>
    </dict>
    …
</dict>
</plist>

api.insecuredomain.com

Первое исключение, которое мы определили, сообщает ATS, что связь с этим поддоменом отменяет требование обязательного использования HTTPS. Оно применяется только к конкретному адресу, указанному в исключении, и не затрагивает поддомены. Не стоит забывать, что ключ NSExceptionAllowsInsecureHTTPLoads относится не только к использованию HTTPS, а и к ATS в целом. Таким образом, для этого домена отменяются все требования App Transport Security.

cdn.domain.com

Возможна ситуация, в которой приложение обращается к серверу, не использующему необходимую версию TLS (1.2 или выше). В этом случае можно определить исключение, указывающее минимальную версию TLS, которая может использоваться. Это будет лучше с точки зрения безопасности, чем полное отключение App Transport Security для этого домена.

otherdomain.com

Ключ NSIncludesSubdomains сообщает App Transport Security, что исключение применяется к каждому поддомену указанного адреса. Кроме того, определяется, что домен может использовать шифры, которые не поддерживают Forward Secrecy (NSExceptionRequiresForwardSecrecy).

Заключение

ATS - это очень хороший инструмент, позволяющий намного лучше и проще контролировать происходящее в вашем приложении. Видно, к каким серверам оно обращается, как они настроены и т.д. Кроме того, эта настройка помогает, в какой-то мере, контролировать состояние серверных частей приложения и поддерживать их безопасную конфигурацию. Да, механизм ATS - не только про клиента, он в какой-то степени заставляет backend придерживаться лучших практик безопасности. Так что перед тем, как просто отключить ATS в приложении, задумайтесь: “Может, пришло время правильно сконфигурировать серверную часть?” Также проанализируйте, какие ресурсы, кроме собственных, вы используете, и почему для них нужно отключать потенциальную защиту?

Конечно, хотелось бы видеть в настройке App Transport Security и другие параметры, например, связанные с SSL Pinning (по аналогии с тем, как это сделано в Android). Это позволило бы не искать свои пути для каждого фреймворка, а задавать это на уровне системы. Но, к сожалению, думаю, что этого мы уже не дождемся.

Надеюсь, что эта статья поможет всем интересующимся понять нюансы настройки App Transport Security, а возможно и заставит правильно его сконфигурировать для своего приложения, вместо полного отключения.

Ссылки

  1. Preventing Insecure Network Connections

  2. HTTPS Server Trust Evaluation

  3. AVFoundation

  4. Supporting App Transport Security

  5. Debugging HTTPS Problems with CFNetwork Diagnostic Logging

  6. WEBVIEWS

  7. Working together to detect maliciously or mistakenly issued certificates

  8. OWASP Mobile Top 10

  9. CWE-319: Cleartext Transmission of Sensitive Information

  10. Безопасность транспорта приложений

  11. Безопасность транспорта приложений в Xamarin. iOS

  12. Подготовка приложения для iOS 9

  13. Working with Apple’s App Transport Security

  14. App Transport Security Has Blocked My Request

  15. Forward secrecy

  16. Info.plist based Certificate Pinning on iOS

  17. Identity Pinning: How to configure server certificates for your app

  18. TLS Pinning with ATS

  19. [SSL Pinning] NSPinnedDomains will be honored by which APIs?

  20. Introducing Safari View Controller

Теги:
Хабы:
+5
Комментарии5

Публикации

Информация

Сайт
swordfish-security.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
SwordfishTeam