Skip to content

Mobile secure data storage

Angelina edited this page Nov 9, 2017 · 24 revisions

Хранение данных на мобильном устройстве с ограничением доступа для конкретного приложения .

Данная статья описывает базовые принципы и решение проблемы безопасного хранения данных в local storage мобильных устройств (android/ios), с быстрым доступом к ним из единственного приложения, без использования центров сертификации.

Понятия и определения

  • local storage (LS) - файл, расположенный на мобильном устройстве, в который осуществляется запись статических данных приложения.
  • master key (MK) - ключ, на основании которого выполняется шифрование данных для LS

Описание проблем

Часто случается, что при работе мобильного приложения, появляются данные, к которым должно быть очень трепетное отношение, например private key от bitcoin кошелька, либо вся база данных. Для лучшего быстродействия и лучшего UI/UX, пользователь должен единожды ввести этот ключ и забыть о его существовании. Отсюда, у разработчиков, появляются ряд проблем:

  1. Злоумышленник достаточно легко может получить доступ к LS нашего приложения.
  2. Из 1 вытекает, что, нам необходимо зашифровывать данные перед записью в LS.
  3. Из 2 вытекает, что, нам необходимо иметь master key и где-то его хранить.
  4. Хранение MK в LS невозможна (проблема курицы и яйца)
  5. Использование Центров сертификации также невозможно, т.к. значительно страдает быстродействие, а при отсутствии подключения к интернету - доступ к MK впринципе.
  6. Использование локального центра сертификации мобильного телефона - добавляет проблемы в самой реализации, ухудшает UX приложения, ставит под угрозу все конфиденциальные данные при взломе локального центра сертификации.
  7. Хардкод MK в самом приложении недопустим из-за возможности реверс инжиниринг.
  8. Можно переложить запоминание MK на пользователя и заставлять его вводить при каждом использовании приложения, но пользователи не в состоянии запомнить более-менее безопасный ключ/пароль.

Логика решения

А есть ли смысл вообще где-то хранить Master key? Что если MK будет генерироваться в исполняем коде самого приложения в тот момент когда он нужен, жить какое-то время в оперативной памяти, и бесследно удаляться. В этом случае мы можем разбить задачу на несколько более мелких, но конкретных проблем:

  • f1() - привязка MK к установочному циклу приложения;
  • f2() - привязка MK к релизному сертификату приложения
  • f3() - привязка MK к конкретному устройству

В конечном итоге:

MK = SHA256( f1() + f2() + f3() );

f1() - Привязка Master Key к установочному циклу приложения

Зачем это нужно?

Любые манипуляции с установкой/удалением приложения должны заканчиваться генерацией абсолютно нового значения. В этом случае мы лишаем злоумышленников возможности, изменив исходных код, добавить логирование MK. Другими словами, если приложение было удалено, то новая его установка, в результате исполнения f1() вернет иное значение, а значит данные зашифрованные MK в предыдущей установке будут в безопасности.

Но при этом мы должны иметь возможность накатывать обновления приложений.

Поэтому решением данной проблемы является получение установочного идентификатора приложения, который:

  • генерируется из кода самого приложения
  • не изменяется при накатке обновления приложения
  • изменяется при удалении и новой установке приложения.
  • не может быть изменен/удалять при пользовательских манипуляциях

Решения:

f2() - привязка MK к релизному сертификату приложения

Зачем это нужно?

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

А т.к. релизные сертификаты находятся под контролем официальных разработчиков, то задача по взлому MK значительно усложняется.

Поэтому,

f2() = SHA256(signCertificate);

Решения:

  • android
PackageInfo info;
    try {
        info = context.getPackageManager().getPackageInfo(
                context.getPackageName(), PackageManager.GET_SIGNATURES);

        for (Signature signature : info.signatures) {
            MessageDigest md = MessageDigest.getInstance(context.getString(R.string.sha));
            md.update(signature.toByteArray());
            return md.digest();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
  • ios

В качестве ключа берется Identifier for Vendor (IDFV)

Cсылка на документацию Apple по identifierForVendor.

UIDevice.current.identifierForVendor!.uuidString

f3() - привязка MK к конкретному устройству

Зачем это нужно?

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

Но эти идентификаторы достаточно легко подделать, поэтому в нашем случае - результат исполнения данной функции стоит воспринимать как соль при вычислении SHA256().

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

Решения:

  • android
Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID)
  • ios

Для iOS берется токен для пуш-уведомлений.

func application(_ application: UIApplication, 
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
        print("push token: \(token)")
        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "deviceTokenNotification"),
                                        object: token)
}

Итог

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

Clone this wiki locally