Пользователь
0,0
рейтинг
12 мая 2014 в 14:41

Разработка → Защита баз данных в iOS



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

При установке приложения внутри директории /var/mobile/Applications/ создается директория, служащая его «песочницей». Ее рамками ограничены практически все возможные действия приложения. Именно здесь хранятся настойки NSUserDefaults, прочие plist файлы, Cookies, а также базы данных и прочие ресурсы, которые могут быть изменены пользователем. Для доступа к этой директории на устройстве не должен быть установлен Jailbreak — это возможно с помощью любого файлового менеджера (например, iFunBox).

В качестве иллюстрации распространенности этой уязвимости возьмем приложение, постоянно мелькающее в платном топе App Store — Smart Safe.

С помощью iFunBox ознакомимся с содержимым папки /Documents. Помимо всего прочего, она содержит в себе следующие файлы: notes.sqlite, notes.sqlite-shm, notes.sqlite-wal. База данных notes.sqlite содержит в себе три таблицы:
  • ZNOTE,
  • Z_METADATA,
  • Z_PRIMARYKEY.

Особенный интерес представляет первая из них. В поле ZDATE содержится дата публикации, в ZBODY – текст заметки, в ZTITLE – ее заголовок. Весь текст не зашифрован и хранится в открытом виде:



Disclaimer
Разработчик был уведомлен об обнаруженной уязвимости, но никаких комментариев и/или исправлений не последовало. Упомянутая уязвимость — не единственная, приложение хранит абсолютно все данные в открытом виде.

Один из способов борьбы с этой уязвимостью — использование фреймворка SQLCipher, поддерживающего прозрачное шифрование на лету. Для всех криптографических функций (AES256, генератор псевдослучайных чисел, PBKDF2 ключи) SQLCipher использует библиотеку OpenSSL. Подробное описание, документация и сборки находятся по этой ссылке.

В рамках статьи рассмотрим использование SQLCipher совместно с FMDatabase — популярной оберткой для работы с SQLite базами данных в Cocoa.

Если вы используете Cocoapods, то для добавления FMDatabase и SQLCipher в проект достаточно добавить строчку pod 'FMDB/SQLCipher' в podfile. Теперь обратимся к AppDelegate.m:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentDir = [documentPaths objectAtIndex:0];
    self.databasePath = [documentDir stringByAppendingPathComponent:@"sqmple.sqlite"];
    
    [self createAndCheckDatabase];
    
    return YES;
}

-(void) createAndCheckDatabase
{
    BOOL success;
    
    NSFileManager *fileManager = [NSFileManager defaultManager];
    success = [fileManager fileExistsAtPath:self.databasePath];
    
    if (success) return; // If file exists, don't do anything
    
    // if file does not exist, make a copy of the one in the Resources folder
    NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"sample.sqlite"]; // File path
    
    [fileManager copyItemAtPath:databasePathFromApp toPath:self.databasePath error:nil]; // Make a copy of the file in the Documents folder
    
    // Set the new encrypted database path to be in the Documents Folder
    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentDir = [documentPaths objectAtIndex:0];
    NSString *encryptedDatabasePath = [documentDir stringByAppendingPathComponent:@"encrypted.sqlite"];
    NSString *key = @"PassKey";
    
    // SQL Query. NOTE THAT DATABASE IS THE FULL PATH NOT ONLY THE NAME
    const char* sqlQ = [[NSString stringWithFormat:@"ATTACH DATABASE '%@' AS encrypted KEY '%@';", encryptedDatabasePath, key] UTF8String];
    
    sqlite3 *unencrypted_DB;
    if (sqlite3_open([self.databasePath UTF8String], &unencrypted_DB) == SQLITE_OK) {
        
        // Attach empty encrypted database to unencrypted database
        sqlite3_exec(unencrypted_DB, sqlQ, NULL, NULL, NULL);
        
        // export database
        sqlite3_exec(unencrypted_DB, "SELECT sqlcipher_export('encrypted');", NULL, NULL, NULL);
        
        // Detach encrypted database
        sqlite3_exec(unencrypted_DB, "DETACH DATABASE encrypted;", NULL, NULL, NULL);
        
        sqlite3_close(unencrypted_DB);
    }
    else {
        sqlite3_close(unencrypted_DB);
        NSAssert1(NO, @"Failed to open database with message '%s'.", sqlite3_errmsg(unencrypted_DB));
    }
    
    self.databasePath = [documentDir stringByAppendingPathComponent:@"encrypted.sqlite"];
}

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

- (FMDatabase *)openWriteableDatabase
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:@"encrypted.sqlite"];
    FMDatabase* database = [FMDatabase databaseWithPath:writableDBPath];
    [database open];
    
    NSString *key = @"PassKey";
    [database setKey:key];
    
    return database;
}

С открытой таким способом базой данных можно совершать любые действия: добавление строк, их удаление и прочее. Конечно, при работе с зашифрованной БД не рекомендуется использовать статичный ключ, зашитый в код приложения. Неплохим альтернативным вариантом будет разработать метод динамической генерации ключа, уникального для каждого устройства (для этого можно использовать комбинацию различных параметров, таких как identifierForVendor или установленный пользователем пароль для доступа к приложению). Смена ключа, используемого БД, происходит так же просто, как и все остальное:

NSString *newKey = @"NewPassKey";
[database rekey:newKey];

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

Конечно, использование SQLCipher не сможет полностью обезопасить данные пользователей — но оно отпугнет начинающих исследователей и усложнит жизнь опытным взломщикам. Напоследок стоит упомянуть тот факт, что для появления в App Store приложения, использующего этот фреймворк, необходимо предоставить форму ERN, процесс получения которой прекрасно описан JacobL в этом посте.
Егор @YourDestiny
карма
35,7
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (14)

  • +1
    Выходит что все на совести создателя приложения. Часто возникало желания начать использовать апп такого рода, но всегда возникали сомнения по поводу безопасности моих паролей и добросовестности создателей. Пока же пользуюсь только родный Кичэйном, но он только для браузера и родных приложений. Но чаще храню в блокноте под подушкой.
    • 0
      Используйте MiniKeePass. Последнюю версию можно собрать из исходников: github.com/MiniKeePass/MiniKeePass
      • +2
        Я не являюсь программистом, и только начинаю познавать мир айти. Поэтому могу оценивать только как пользователь. И простота в использовании и интергация играют важную роль.
      • 0
        Можете рассказать пару слов об MiniKeePass? Я что-то не понял, как это работает. Вот я собрал его из исходников, как вы говорите, но чтоб залить на девайс мне ведь понадобится девелоперская подписка?
        • 0
          Чтобы самому сделать сборку — да, нужен девелоперский аккаунт. Если с этим проблема — можете скачать версию 1.4.8 из App Store. Ссылку отправил в личку.
  • 0
    Data Protection Framework не помогает?
    • +1
      Не совсем то, что нужно. Насколько я помню, данные с его помощью шифруются только когда прошло несколько минут с момента блокировки паролем. Все остальное время, включая также и тот вариант, что пароль не установлен вообще, информация хранится в открытом виде и может быть получена тем же самым образом.
      • 0
        Просто IMHO именно DPF устанавливает правильный способ защиты персональных данных — а именно passcode/fingerprint lock самого устройства. Из залоченного девайса никто данные не прочтет. Все остальное — это компромиссы и костыли разной степени кривизны.
        Вот, кстати, интересный документ: images.apple.com/ipad/business/docs/iOS_Security_Feb14.pdf
        • +1
          Я считаю, что нельзя полностью полагаться на чувство сознательности пользователя, который мог и не устанавливать блокировку, особенно приложениям, оперирующим конфиденциальными данными. Да и вообще, полностью полагаться на встроенные механизмы безопасности — отнюдь не лучшее решение. А использование проверенных сторонних библиотек считать костылем — как минимум странно.
        • +1
          Все остальное — это компромиссы и костыли разной степени кривизны.

          Не забудьте юзеру об этом сообщить в качестве оправдания отсутствия защиты его данных.
          А злоумышленнику оставьте записку «пожалуйста, не ломайте базу».
  • +1
    Есть версия от sqlite:
    www.sqlite.org/see/doc/trunk/www/index.wiki

    Она не тянет за собой openssl — тк с openssl проблемы с публикацией могут быть.
    И да — без пин кода на вход в программу тут не обойтись — ведь ключ на чем-то надо шифровать.
    По мне это надежнее чем использовать встроенные API, тк тут ты знаешь с чем имеешь дело.
    Ну и вот версия бесплатная:
    github.com/shenghe/FreeSQLiteEncryption
    • 0
      Спасибо за ссылки, попробую поиграться. В SQLCipher огромный плюс — встроенная поддержка в FMDatabase, который все-таки очень удобен при разработке.
      Насчет проблем с публикацией — не возникло, учитывая, что ERN приложил.
  • +2
    Стоит напомнить, что при использовании Core Data проблема секурности также остается актуальной.
    • 0
      Несколько ссылок привели вот к этой библиотеке: github.com/project-imas/encrypted-core-data. Честно скажу — еще не потестил, но, судя по описанию, позволяет прикрутить прозрачное шифрование на базе SQLCipher к Core Data.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.