Ssl pinning что это
Что такое безопасность соединения или включите SSL-pinning в вашем мобильном приложении прямо сейчас
Feb 27, 2020 · 6 min read
В этой статье я кратко расскажу, что означает «безопасное соединение» между клиентом и сервером, что такое SSL-pinning, для чего он нужен и когда его использовать. Так же объясню от каких угроз мы защищаемся с его помощью.
Безопасность соединения
Представим, что у нас есть сайт или мобильное приложение (оба по д падают под определение клиента). При обращении сайта или мобильного приложения к бекенду (сервер) безопасность соединения обеспечивается с помощью протокола SSL (Secure Socket Layer), а если быть точнее, TLS (Transport Layer Security). TLS ставит своей целью создание между двумя узлами сети защищённого от прослушивания и подмены информации канала связи, а также проверку того, что обмен данными происходит между именно теми узлами, т.е. обеспечение конфиденциальности, целостности и аутентификации. Под узлами можно понимать сервер и мобильное приложение, либо сервер и веб-браузер.
Немного истории про SSL/TLS
Изначально SSL был разработан компанией Netscape для своего одноименного браузера в середине 90-х. Позднее на основании SSL 3.0 был принят RFC-стандарт TLS 1.0. Рекомендованными для применения в 2019 году являются версии TLS не ниже 1.2 (актуальная версия на сегодня — 1.3). На странице в википедии можно посмотреть поддержку версии TLS у всех популярных браузеров.
Человек посередине
MITM (Man in the middle) — данный вид атаки направлен на «прослушку» или изменение трафика между двумя узлами (клиентом и сервером). В случае использования незащищенного HTTP это не составляет труда, тогда как в случае HTTPS (HTTP + SSL, или HTTP secure) данные зашифрованы и обычный просмотр пакетов трафика не даст никакой полезной информации злоумышленникам об их содержании. О том, каким образом шифруются данные, поговорим дальше.
Симметричное, ассиметричное шифрование
Симметричное шифрование — вид шифрования, при котором для шифрования и дешифрования будет использоваться один и тот же ключ. Наиболее стойким на данный момент является шифр AES (Advanced Encryption Standard), длина ключа может быть 128, 192 или 256 бит. Из минусов симметричного шифрования — проблема передача ключа, ведь в случае его утери, им может воспользоваться злоумышленник. В случае TLS используется ассиметричное шифрование — когда для шифрования используется публичный ключ, а для дешифрования приватный. Публичный ключ передаётся от узла 1 к узлу 2, и даже в случае его перехвата злоумышленником, содержимое данных невозможно расшифровать, т.к. закрытый ключ не передается между узлами. Наиболее распространённый шифр этого типа — RSA (Rivest — Shamir — Adleman), размер ключа от 1024 до 4096 бит. Удобство ассиметричных шифров так же в том, что публичный ключ может быть передан большому количеству узлов без угрозы его утечки, как в случае ключа симметричного шифра. Из минусов — асимметричные шифры значительно медленнее симметричных. Поэтому в TLS используется смешанный подход — все данные шифруются симметрично, а набор ключей асимметрично. Но так как узлы шлют информацию в обе стороны, необходимо шифровать трафик с двух сторон. На помощь приходит протокол Диффи-Хеллмана. Первые 2 минуты видео достаточно понятно объясняют принцип его работы. Если мы хотим защитить соединение по HTTP, здесь стоит подумать о SSL-сертификате.
Сертификат X.509
В качестве удостоверяющего хост документа выступает сертификат стандарта X.509. Под сертификатом можно понимать файл, в котором содержится следующая информация:
При нажатии на иконку замка в строке сайта
Сертификаты не существую сами по себе — их выпускают центры сертификации (Certificate Authority), в цепочке сертификатов самым первым является сертификат CA, его еще называют корневой. CA являются общеизвестными и ключи, которые они выпускают являются доверенными по умолчанию. Корневые сертификаты как правило “зашиваются” в операционную систему и обновляются при следующих обновлениях. Сертификаты выстраиваются в цепочку, можно так же назвать термин дерево: сертификаты первого уровня (выпущенные CA), так же могут выпускать сертификаты следующих уровней. В случае компрометации сертификата, он может быть отозван, и тогда автоматически отзываются все дочерние сертификаты.
Человек посередине по TLS
Кажется, что TLS решает атаку MITM. Тем не менее, возможен вариант, когда между узлами окажется самоподписанный корневой сертификат злоумышленника и каким-то образом этот сертификат попадёт к нам на устройство. Например, если пользователь недостаточно технически образован и ему предлагается установить сертификат для пользования сетью Wi-Fi. После этого данные шифруются сертификатом злоумышленника (которые он легко расшифровывает) и мы не подозреваем о подмене. В данном случае злоумышленник выступает неким прокси-сервером между нашими узлами.
SSL-pinning
Если мы реализуем проверку сертификата на клиенте, то можем отловить ситуацию, когда сертификат между узлами неподлинный. Технически это выглядит следующим образом: при установлении соединения с хостом на клиенте проверяется, соответствует ли сертификат от сервера тому сертификату, о котором знает клиент. Если нет — соединение считается небезопасным и обмен данных с хостом прекращается. Помимо прослушивания трафика, SSL-pinning так же не раскрывает спецификацию запросов API, что может пригодиться при атаках как на сервер, так и на клиент.
Схема работы SSL-pinning
Как проверять сертификат или виды SSL-pinning
По тому, какой сертификат проверяем:
По тому, какие данные из сертификата проверяем:
Фингерпринт виден в том же окне просмотра сертификата в Safari
Когда SSL-pinning не нужен
Вместо вывода
Раз уж мы обсудили SSL-pinning в контексте мобильных приложений, почему бы не сделать тоже самое с веб-сайтами? Ответ достаточно прост — браузер посещает большое количество сайтов, ровно как и каждый день появляется много новых, и это достаточно проблематично обладать информацией обо всех них. Мобильное приложение же, как правило, обращается к конечному числу доменов, информация о сертификатах хранится внутри него. В 99% случаев не нужно придумывать свои способы шифрования, TLS + SSL-pinning обеспечивают достаточный высокий уровень безопасности для передачи чувствительных данных по сети. Примеры приложений, где SSL-pinning является по-моему мнению обязательным — это мобильные приложения банков, другие приложения из финансового и страхового сектора. Для более подробного погружения в схему работы TLS советую данный ресурс.
SSL pinning во Flutter
Для большинства мобильных приложений рано или поздно приходит время роллаута. И если к этому моменту вы ещё не позаботились хотя бы о минимальной защите от атак, то чем ближе будет подходить время массового запуска, тем острее будет стоять данный вопрос.
Для мобильных приложений, работающих с сервером и конфиденциальными данными, однозначно стоит позаботиться о безопасности своих пользователей от довольно популярной атаки — MITM.
Давайте разберёмся, что же это за атака. Атака посредника, или атака «человек посередине» (англ. Man in the middle) — вид атаки в криптографии и компьютерной безопасности, когда злоумышленник тайно ретранслирует и при необходимости изменяет связь между двумя сторонами, которые считают, что они непосредственно общаются друг с другом. Является методом компрометации канала связи, при котором взломщик, подключившись к каналу между контрагентами, осуществляет вмешательство в протокол передачи, удаляя или искажая информацию.
Изначально протокол Http является уязвимым для данного типа атаки и для исправления данной уязвимости была создана защищённая версия этого протокола Https. Он использует SSL или TLS для шифрования соединения между клиентом и сервером. Работают эти схемы следующим образом: в начале общения сервер посылает клиенту свой сертификат, чтобы клиент идентифицировал его. Данный сертификат стандарта X.509 является файлом, в котором содержится следующая информация:
Поскольку установка каждого сертификата отдельно была бы просто невозможна, ведь их огромное количество, работает следующая система управления сертификатами. Так как сертификаты являются фактически удостоверениями серверов, то они не появляются сами — их выпускают центры сертификации (Certificate Authority). Самыми важными являются сертификаты CA, их еще называют корневыми. CA являются общеизвестными, и их ключи являются доверенными по умолчанию. Они как правило встраиваются в операционную систему и обновляются при следующих обновлениях. Сертификаты работают по строгой иерархии. Сертификаты СА могут подписывать другие сертификаты, они в свою очередь подписывают сертификаты следующего уровня и так далее. В случае компрометации сертификата, он может быть отозван и вместе с ним автоматически отзываются все дочерние сертификаты.
Выглядит довольно надёжно, но что если пользователь сам скомпрометирует хранилище сертификатов в своей системе, установив в доверенные сертификат злоумышленника.
SSL-pinning
SSL-pinning – привязка сертификата или публичного ключа сервера к клиенту. В случае с мобильным приложением, одним из эффективных способов является внедрение в приложение SSL сертификата, которому мы собираемся доверять. В данном случае мы игнорируем хранилище системы и сами определяем какому сертификату мы будем доверять. Этот способ может помочь нам при необходимости использовать самоподписанный сертификат, не утруждая конечного пользователя его установкой.
SSL-pinning в Dart
Http запросы в Dart осуществляются при помощи класса HttpClient, точнее при помощи его приватной стандартной реализации. Каким будет подключение обычным или защищённым определяется исходя из схемы адреса к которому мы подключаемся.
bool isSecure = (uri.scheme == «https»);
В случае использования защищённого соединения, будет использован SecureSocket, который попытается установить защищённое соединение. При неудаче нас уведомят, что соединение использует скомпрометированный сертификат, если мы заранее зарегистрировали функцию обработки данной ситуации.
Регистрируется она с помощью поля badCertificateCallback у HttpClient.При помощи этой функции мы можем не только узнать, что сертификат скомпрометирован, но и обработать данный момент. Надо принять решение — продолжаем мы данный запрос или нет.
Если мы не станем добавлять обработчик, соединение будет разорвано при провале стадии проверки сертификата.
На что же опирается SecureSocket во время проверки сертификата? Стандартное поведение в данном случае — проверка с учётом тех сертификатов, которые зарегистрированы в системе. Как мы уже определили раньше, не самое лучшее решение для надёжной проверки. Нам необходима возможность проверки именно конкретного сертификата или цепочки сертификатов.
В реализации dart за это отвечает класс SecurityContext. Исходя из его описания мы узнаём, что он является объектом, хранящим сертификаты, которым доверяют при создании безопасного клиентского соединения, а также цепочку сертификатов и закрытый ключ для обслуживания с безопасного сервера. Этот объект можно передать HttpClientпри создании, и в таком случае, клиент будет опираться в проверки именно на данные, хранящиеся в контексте, игнорируя доверяет или нет этим сертификатам система.
SecurityContext может создаваться через различные конструкторы и в зависимости от этого будет иметь разное содержание. Например, дефолтный конструктор создаст абсолютно пустой контекст, без зарегистрированных ключей.
Конструктор же SecurityContext.defaultContext создаст контекст, который содержит некоторые наборы доверенных сертификатов, в зависимости от системы. В случае Windows или Linux будут использованы сертифика хранилища Mozilla Firefox. В случае MacOS, iOS, Android будут зарегистрированы сертификаты, которым доверяет система.
Помимо этого контекст имеет методы для регистраций приватного ключа, доверенного сертификата и цепочки сертификатов.
Для регистрации доверенного сертификата предлагается использовать один из двух методов.
Данный метод позволяет указать путь до сертификата.
Этот же метод использует для регистрации содержание сертификата в байтовом представлении.
Даже если не обращать внимание на предупреждение разработчиков:
NB: This function calls [File.readAsBytesSync], and will block on file IO. Prefer using [setTrustedCertificatesBytes],
мне кажется более предпочтительным использование setTrustedCertificatesBytes. Причина довольно проста: если какой-то «нехороший человек» захочет навредить нам, ему для этого потребуется гораздо больше ресурсов. Если мы положим файл сертификата в обычном виде в ресурсы, при декомпиляции он сможет легко найти его и подменить. Да, он не сможет самостоятельно подписать пересобранное приложение, но никто не вредит пользователю с таким же усердием, как сам пользователь. Поэтому факт невозможности переподписать приложение теми же ключами, может не стать решающим фактором.
Что если мы зашьём байтовое представление ключа в виде кода в приложение?
Flutter при сборке приложения в release режиме использует AOT компиляцию, поэтому мы создадим довольно много проблем тому, кто захочет просто подменить сертификат, вскрыв приложение.
Практическая реализация SSL-pinning
Во-первых, для установки доверенного соединения вам потребуется сам файл сертификата, который использует сервер. Если вы по какой-то причине не можете обратиться к системным администраторам, или нет времени ждать пока вам его предоставят, можете довольно легко получить его с помощью браузера. В данном примере я разберу вариант с Google Chrome, но с таким же успехом можете сделать это в большинстве других браузеров.
Переходим по адресу, сертификат которого собираемся получить. Например www.facebook.com. Если соединение защищено, в адресной строке мы увидим изображение замка, при нажатии на который мы откроем информацию о соединении.
Нас интересует сертификат, переходим внутрь.
В открывшемся окне мы видим цепочку подписания сертификатов. Конечный сертификат и будет принадлежать данному ресурсу.
Перетащив сертификат в любую директорию на своём компьютере вы сохраните файл сертификата.
Другой способ получить тот же самый файл сертификата.
В инструментах разработчика переходим на вкладку Security. Нажатие на View Certificate открывает уже знакомое нам окно просмотра сертификата, откуда его мы сохраняем на компьютер.
В случае если формат не подходит, придётся конвертировать. Для конвертации можно воспользоваться любым из сервисов, я рассмотрю один из способов.
В командной строке на уровне, где находится файл сертификата выполняем команду:
В которой certificate.cer имя сертификата, который конвертируется, а certificate.pem имя сертификата после конвертации.
Итак, у нас есть сертификат в нужном нам формате. Нам нужно прочитать его в виде байтов и сохранить это значение, чтобы в дальнейшем использовать в приложении. Для хранения этих данных вполне подойдёт List. Для удобства я написал небольшой скрипт, который считывает сертификат в виде набора байт и создаёт Dart файл с переменной, хранящей данное значение.
Теперь нам нужно лишь скопировать данный файл в проект. Использование в проекте будет довольно простым.
Если мы попытаемся обратиться по адресу, и сертификат предоставленный им при проверке будет не совпадать с тем, который мы зашили в приложение, будет выброшена ошибка HandshakeException.
Заключение
Надеюсь, эта статья будет полезна при разработке приложений и поможет лучше защитить ваших пользователей от данного типа атак.
Откручивание SSL пиннинга в Android приложениях
Проблемы перехвата трафика
В процессе тестирования на проникновение мобильных приложений на Android часто необходимо выяснить, каким образом приложение общается с сервером, с какими адресами происходит взаимодействие, как выглядят запросы, какие данные передаются. Но не всегда это удается сделать.
В наше время для взаимодействия компонентов веб-приложений используется протокол HTTPS, в основе которого лежат протоколы HTTP и TLS. Просто так перехватить трафик приложения не выйдет, т.к. он зашифрован. Можно, конечно, использовать прокси сервер, который с помощью своего сертификата сможет расшифровать трафик приложения и увидеть все запросы. Однако и средства защиты приложений не стоят на месте. Многие мобильные приложения используют SSL Pinning.
SSL Pinning – это внедрение SSL сертификата, который используется на сервере в код мобильного приложения. Таким образом, приложение не использует хранилище сертификатов устройства и не будет работать с сертификатом, который мы ему подсунули.
Ошибка, которая возникает в случае использования Burp suite для перехвата трафика с помощью собственного сертификата
Способы защиты приложений
Для того чтобы понять, как обходить защиту, необходимо сначала разобраться, какими средствами эта защита осуществляется. Для этого существуют несколько способов.
Для данного способа необходимо добавить файл сертификата в файлы приложения, затем создать KeyStore и добавить в него наш сертификат.
После этого создаем сам TrustManager, который будет работать с нашим KeyStore, в котором находится нужный сертификат.
Далее создается SSLContext, который использует наш TrustManager. Затем указываем для URLConnection SocketFactory из созданного SSLContext.
Основная суть этого метода в том, что мы используем непосредственно сам файл сертификата в проекте, а затем создаем TrustManager, который будет работать только с этим сертификатом.
Данная реализация работает непосредственно с API на достаточно низком уровне, поэтому этот способ надо использовать осторожно.
Вторым способом является использование библиотеки OkHttp. В ней есть удобный CertificatePinner, который с помощью своего конструктора закрепляет за доменом определенный fingerprint сертификата.
Этот fingerprint высчитывается из сертификата, с которым должно работать приложение, а затем пишется непосредственно в код.
Можно указать несколько сертификатов для разных доменов.
Network Security Configuration
С версии Android 7.0 стала возможна настройка конфигурации сети. В папке res/xml/ создается файл network_security_config.xml, в котором прописываются правила и указываются fingerprints, как в случае с OkHttp.
Затем в AndroidManifest.xml прописывается использование данного файла в качестве android:networkSecurityConfig.
Этот способ позволяет редактировать сертификаты, не меняя исходный код приложения.
Способы обхода механизмов защиты
Frida – инструмент для динамического внедрения кода в приложение. Утилита прямо в процессе работы приложения способна отслеживать вызовы методов приложения, модифицировать методы приложения, обращаться к памяти выполняемого приложения и многое другое.
Для обхода SSL Pinning с помощью Frida есть несколько готовых скриптов, создающих кастомный TrustManager, который будет доверять нашему сертификату. Рассмотрим скрипт.
CertificateFactory и X509Certificate нужны для создания экземпляра сертификата из считанного файла.
FileInputStream и BufferedInputStream используются для считывания файла сертификата.
KeyStore – наш кастомный KeyStore, в который положим наш сертификат.
TrustManagerFactory создает экземпляр TrustManager, который будет работать с нашим KeyStore.
SSLcontext – реализация протокола SSL, которая выступает как factory для sslSocketFactory.
Считываем сертификат cert-der.crt, который мы предварительно поместили на устройство, и создаем экземпляр этого сертификата.
Создаем keyStore, в который складываем наш сертификат.
Создаем trustManager, который доверяет keyStore с нашим сертификатом.
Вот здесь начинается настоящее использование Frida. Данный фрагмент перезаписывает метод SSLContext. Теперь при вызове приложением метода сначала будет вызываться информационный лог в консоль, затем выполнится исходный метод, который мы перезаписываем. Но он вызовется не просто так. Вместо второго параметра, который будет передан в функцию, новая функция подставляет кастомный TrustManager с нашим сертификатом. И в конце снова информационный лог в консоль о том, что все прошло удачно.
Таким образом, данный скрипт готовит TrustManager, а затем использует его в методе вместо того, который передается (переменные a и с передаются в вызов исходной функции, а вот переменная b заменяется на кастомный TrustManager).
Frida позволяет переопределять методы, выполнять собственные скрипты. Поэтому с помощью Frida можно обойти защиту, реализованную через OkHttp CertificatePinner.
Главное преимущество этого метода – отсутствие необходимости патчить исходный код apk и пересобирать приложение. А так как нам не нужно пересобирать приложение, значит этому способу не помешают методы защиты приложения от пересборки.
С другой стороны, для данного метода необходимо подключить телефон к устройству с Frida, установить frida-server на телефон, а это не всегда удобно.
Изменение исходного кода и пересборка apk
В случае, если мы хотим один раз произвести обход средств защиты и получить приложение, в котором не будет SSL Pinning, необходимо изменить сам код приложения.
Для декомпиляции можно воспользоваться удобной утилитой apktool. Также для декомпиляции работы с разобранным apk, а затем и для сборки можно использовать удобное расширение для Visual Studio Code ApkLab.
Чтобы не копаться в трудночитаемых исходных кодах smali, вышеперечисленные утилиты умеют преобразовывать их в Java-код, который уже гораздо удобнее изучать. Также утилиты предоставляют функциональность деобфускации, которая очень помогает в изучении исходного кода.
ApkLab предоставляет также автоматизированный поиск необходимых участков кода. Рассмотрим что и почему он изменяет.
В первом файле утилита закомментировала исполнение следующих методов: checkClientTrusted, checkServerTrusted, getAcceptedIssuers.
По названиям методов можно предположить, что первый проверяет, является ли клиент доверенным, второй проверяет, является ли сервер доверенным, а третий получает лист доверенных сертификатов. Но не будем гадать и перейдем к Java-коду данных методов.
Теперь мы можем уже с пониманием рассмотреть, почему утилита закомментировала эти фрагменты. Первые два метода внутри ничем не отличаются и вызывают новый метод mo9499a (такое название из-за деобфускации), который слишком большой, чтобы вставлять его здесь. Но, судя по документации, этот метод строит цепочку сертификатов до корневого сертификата, а потом проверяет, можно ли ему доверять. В данном методе при ошибке будет выброшено исключение. Но мы закомментировали вызов данной функции, поэтому никаких проверок сертификат проходить не будет, и поэтому исключений мы не получим.
Метод getAcceptedIssuers должен возвращать массив доверенных сертификатов. Но дальнейшая реализация и использование данного метода работает таким образом, что если мы вернем пустой массив, то программа будет доверять любому сертификату. Именно это и сделала утилита: закомментировала код, в котором происходит получение сертификатов из keyStore (это происходит в методе m7931a), а вместо этого просто возвращается пустой массив.
Автоматический редактор кода заменяет эти фрагменты, т.к. они используются в проверке сертификата. Поэтому, если необходимо самому изменить какие-то фрагменты проверки, сначала лучше изучить, как работают те или иные методы, а затем уже вручную менять их в smali. К тому же обычно не приходится писать много кода вместо методов. В рассмотренном выше примере не делается ничего сложнее комментирования основной функциональности и создания каких-либо переменных-заглушек.
Также можно рассмотреть OkHttp CertificatePinner. В обзоре реализации был представлен фрагмент кода, где в OkHttpClient передается CertificatePinner. Найдя в smali соответствующий метод, можно убрать строчку, в которой CertificatePinner складывается в поле объекта OkHttpClient.
Изменение NSC (Network Security Configuration)
Самый простой способ обхода SSL Pinning.
В случае, если в приложении присутствует NSC, необходимо его изменить. В файле NSC могут быть прописаны base-config и domain-config.
В base-config может быть указано, каким сертификатам приложение может доверять.
В domain-config настраиваются домены.
Для обхода защиты достаточно лишь убрать весь блок .
ApkLab также умеет делать автоматическое изменение NSC файла.
В случае отсутствия файла в проекте утилита создает его с указанным выше содержимым, а также прописывает его в AndroidManifest.xml.
Подмена файла сертификата
Поэтому можно просто положить туда свой сертификат вместо того, который установили туда разработчики.
В связи с тем, что SSL Pinning может быть реализован множеством разных способов, от различных библиотек, до кастомных реализаций привязки сертификата, задача его обхода не всегда простая и очевидная. Каждый раз, рассматривая новое приложение, нужно изучить, какие методы используются, а затем уже применять соответствующие способы обхода, которые тоже не всегда удобны и работоспособны.
Frida достаточно сильный инструмент, но возникает неудобство в виде постоянного подключения телефона к устройству, предварительной конфигурации и прочего. А если в интернете нет готового скрипта для нужных целей, то написание собственного требует хорошего понимания работы Frida.
Непосредственный патчинг apk хорош тем, что его можно сделать всего раз и на выходе будет приложение без SSL Pinning. Однако на этом пути может помешать защита приложения от пересборки. Конечно и ее можно попытаться обойти, раз мы имеем исходный код приложения, но это потребует больше времени и сил.
Изменение Network Security Configuration достаточно простой и рабочий способ, но редко когда приложения ограничиваются только этим способом защиты. Поэтому знание обхода этого способа SSL Pinning необходимо, но недостаточно.
Подмена KeyStore с доверенным сертификатом в файлах приложения тоже легко реализуется. Но опять же данный способ может не сработать в случае, если приложение при запуске проверяет, не было ли каких-либо изменений в файлах приложения (например, высчитывает чек-сумму).
Таблица 1. Область применения
Способ обхода
Способы защиты, которые можно обойти