Утечка конфиденциальных данных при кешировании сетевых запросов на платформе iOS

    Критичным аспектом в контексте информационной защищенности пользовательских данных является конфиденциальность передаваемых данных в процессе взаимодействия защищаемого приложения с веб-сервисами. Например, утрата конфиденциальности в приложениях, манипулирующих номером банковской карты и кодом аутентификации CVV2/CVC2, представляется уязвимостью критичной степени важности. Одной из распространенных ошибок при разработке защищенных приложений для мобильной платформы iOS, приводящих к частичной или полной утрате конфиденциальности передаваемых данных, является чрезмерное кэширование межсетевых запросов.

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

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


    Испытание боем


    Рассмотрим пример неправильного использования кэша межсетевых запросов. В результате исследования Приложения В в контексте анализа защищенности было установлено, что приложением кэшируются HTTP-запросы, осуществляемые в рамках взаимодействия с основным веб-сервисом. Межсетевые запросы сохраняются в локальной базе данных SQLite в домашней директории приложения по относительному адресу Library/Caches/<application.c>/Cache.db. Схема базы данных приводится ниже.
    Схема базы данных
    CREATE TABLE cfurl_cache_schema_version(schema_version INTEGER);
    CREATE TABLE cfurl_cache_response(entry_ID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, version INTEGER, hash_value INTEGER, storage_policy INTEGER, request_key TEXT UNIQUE, time_stamp NOT NULL DEFAULT CURRENT_TIMESTAMP);
    CREATE TABLE cfurl_cache_blob_data(entry_ID INTEGER PRIMARY KEY, response_object BLOB, request_object BLOB, proto_props BLOB, user_info BLOB);
    CREATE TABLE cfurl_cache_receiver_data(entry_ID INTEGER PRIMARY KEY, receiver_data BLOB);
    CREATE INDEX request_key_index ON cfurl_cache_response(request_key);
    CREATE INDEX time_stamp_index ON cfurl_cache_response(time_stamp);
    CREATE INDEX proto_props_index ON cfurl_cache_blob_data(entry_ID);
    CREATE INDEX receiver_data_index ON cfurl_cache_receiver_data(entry_ID);
    


    Столбец request_key таблицы cfurl_cache_response базы данных хранит идентификаторы сетевых ресурсов (Uniform Resource Identifier, URI), доступ к которым был запрошен приложением, включая запросы по протоколу HTTPS. Такие запросы могут включать в себя конфиденциальные данные пользователя, в том числе аутентификационные данные пользователя. Так, например, приложение В использует квази-RESTful интерфейс для взаимодействия с веб-сервисом и осуществляет GET-запрос для авторизации пользователя.

    sqlite> select request_key from cfurl_cache_response;
    ...
    https://example.com/api/1.0/auth?login=Selma_Nagel&password=qwerty
    ...
    

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

    sqlite> select receiver_data from cfurl_cache_receiver_data;
    ...
    <input data-val="true" ...="" id="CardNumber" name="CardNumber" type="hidden" value="1231 1231 1231 1234">
    <input data-val="true" ...="" id="CardCode" name="CardCode" type="hidden" value="321">
    ...
    

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

    Как такое стало возможно?


    За кэширование межсетевых запросов в рамках мобильного приложения, осуществляемых объектами класса NSURLConnection, отвечает объект класса NSURLCache, доступный через метод +sharedURLCache. Объекты класса NSURLCache содержат два параметра, регулирующих объем контейнера в динамической памяти (In-Memory Cache) и на запоминающем устройстве (On-Disk Cache). Подробнее о классе NSURLCache можно почитать в официальной документации.

    На операционных системах iOS v4.x и ниже кэширование межсетевых запросов на запоминающем устройстве не производилось, а параметр класса diskCapacity игнорировался. Начиная с iOS версий 5 и выше, приложение по умолчанию создает кэш HTTP-запросов на жестком диске мобильного устройства, ограниченный емкостью в 20 Мб. А начиная с 6-й версии iOS, реализована поддержка кэширования запросов по протоколу HTTPS.

    Представляется целесообразным упомянуть, что уязвимости подвержены все мобильные приложения для платформы iOS, явным или неявным образом осуществляющие межсетевые запросы с помощью NSURLConnection. В частности, классы межсетевых запросов популярной библиотеки AFNetworking представляют собой обертки над NSURLConnection. Таким образом, мобильные приложения, использующие AFNetworking, также являются потенциально уязвимыми.

    Как защитить приложение?


    Наиболее простой метод защиты от кэширования запросов, включающих конфиденциальную информацию — установить значение diskCapacity объекта sharedURLCache равным 0.

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        ...
        [[NSURLCache sharedURLCache] setDiskCapacity:0];
        ...
    }
    

    С использование делегатов это можно сделать так.

    - (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                      willCacheResponse:(NSCachedURLResponse *)cachedResponse
    {
        /* Implement cacheing logic here. */
        return nil; // HACK: just do not cache
    }
    

    Тонкая настройка с Cache-Control


    Решения, указанные выше, вряд ли кто-нибудь назовет изящными (скорее наоборот). Они подойдут для быстрого исправления логики приложения, однако для более тонкой настройки взаимодействия клиентского приложения с веб-сервисом мы можем прибегнуть с изменениям политики кэширования запросов на серверной стороне. Политика кэширования устанавливается в HTTP-заголовке Cache-Control (RFC 2616).

    Для демонстрации поведения NSURLCache в зависимости от политики кэширования веб-ресурсов мы собрали простое приложение для мобильной платформы iOS. Был использован пакет iOS SDK v7.0, а приложение было запущено на симуляторе устройства.


    Рис. 1. Работа тестового приложения

    Результаты нашего маленького исследования в приводятся в таблице 1. Обратите внимание на то, что вопреки распространенному мнению, приложением кэшируются межсетевые запросы даже без установленного значения заголовка Cache-Control.

    Ресурс Cache-Control Кэшируется
    /htbin/mr must-revalidate Да
    /htbin/nocache no-cache Да
    /htbin/nostore no-store Нет
    /htbin/private private Да
    /htbin/public public Да
    /htbin/woheader Не указан Да
    /htbin/implicit max-age=604800 Да

    Табл. 1. Кэширование запросов на диске в зависимости от Cache-Control

    Таким образом, наиболее разумным решением, направленным на устранение выявленной уязвимости представляется установка значения Cache-Control равным no-store для запросов, содержащих конфиденциальную информацию. Учитывая семантику значения no-store, можно с уверенностью утверждать, что большая часть клиентских приложений не будут кэшировать такие запросы.

    RFC 2616, пункт 14.9.2
    14.9.2 What May be Stored by Caches

    no-store
    The purpose of the no-store directive is to prevent the inadvertent release or retention of sensitive information (for example, on backup tapes). The no-store directive applies to the entire message, and MAY be sent either in a response or in a request. If sent in a request, a cache MUST NOT store any part of either this request or any response to it. If sent in a response, a cache MUST NOT store any part of either this response or the request that elicited it. This directive applies to both non- shared and shared caches. «MUST NOT store» in this context means that the cache MUST NOT intentionally store the information in non-volatile storage, and MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible after forwarding it.
    Even when this directive is associated with a response, users might explicitly store such a response outside of the caching system (e.g., with a «Save As» dialog). History buffers MAY store such responses as part of their normal operation.
    The purpose of this directive is to meet the stated requirements of certain users and service authors who are concerned about accidental releases of information via unanticipated accesses to cache data structures. While the use of this directive might improve privacy in some cases, we caution that it is NOT in any way a reliable or sufficient mechanism for ensuring privacy. In particular, malicious or compromised caches might not recognize or obey this directive, and communications networks might be vulnerable to eavesdropping.


    Заключение


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

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

    На уровне приложения данная потенциальная уязвимость закрывается с помощью явного запрета локального кеширования при создании NSURLConnection. На уровне сервера данная потенциальная уязвимость должна закрываться указанием на запрет кеширования в HTTP-заголовках ответов сервера для всех запросов, содержащих конфиденциальные данные.
    Инфосистемы Джет
    340.57
    Системный интегратор
    Share post

    Comments 10

      +3
      Если уж и кешировать сетевые запросы, то понятно, что не нужно кешировать конфиденциальные данные КО!
        +1
        Спасибо за замечание. Идеей статьи было продемонстрировать, что поведение NSURLConnection является потенциально уязвимым. Кэширование происходит совершенно прозрачно (т.е. неявно) для разработчика и поэтому может стать причиной утечки конфиденциальных данных, например, вследствие неосведомленности разработчика о тонкостях поведения NSURLCache.
      • UFO just landed and posted this here
          0
          лол, а чем пост защищенней? тем что в урле не палится? очень странное у вас представление о безопасности
            0
            В целом, POST лучше. Это если рассматривать общий случай:

            1) пароли не сохраняются в дефолтовые логи апача
            2) пароли не сохраняются в истории браузера
            3) пароли не рискуют утечь через «Referer:», хотя зависит от ряда условий конечно, но все же…

            Это в общем случае для юзер-форм аутентификации.

            В REST API, более значима проблема 1). То есть надо париться с логингом, что бы пароли не сохранять на сервере в открытом виде.
            +1
            Грубо говоря:

            GET /page/?password=qwerty HTTP/1.1
            Host: bestonlinefreesecurity.com

            vs

            POST /page/ HTTP/1.1
            Host: bestonlinefreesecurity.com
            password=qwerty

            Безопасность!
            • UFO just landed and posted this here
                0
                бороться надо не с последствиями :)
                • UFO just landed and posted this here
                0
                ага, а в коде mysql_query(«select * from users where password=». $_REQUEST[password])

            Only users with full accounts can post comments. Log in, please.