APP安全機制(八)—— 偏好設(shè)置的加密存儲

版本記錄

版本號 時間
V1.0 2017.08.29

前言

在這個信息爆炸的年代,特別是一些敏感的行業(yè),比如金融業(yè)和銀行卡相關(guān)等等,這都對app的安全機制有更高的需求,很多大公司都有安全 部門,用于檢測自己產(chǎn)品的安全性,但是及時是這樣,安全問題仍然被不斷曝出,接下來幾篇我們主要說一下app的安全機制。感興趣的看我上面幾篇。
1. APP安全機制(一)—— 幾種和安全性有關(guān)的情況
2. APP安全機制(二)—— 使用Reveal查看任意APP的UI
3. APP安全機制(三)—— Base64加密
4. APP安全機制(四)—— MD5加密
5. APP安全機制(五)—— 對稱加密
6. APP安全機制(六)—— 非對稱加密
7. APP安全機制(七)—— SHA加密

偏好設(shè)置存儲信息的隱患

大家都知道,存儲可以有很多種方式,其中一種就是偏好設(shè)置進(jìn)行存儲,但是在存儲之前如果不進(jìn)行任何加密的話,那么黑客只要是將沙盒攻陷獲取里面的信息,你的數(shù)據(jù)就裸奔了,你的敏感數(shù)據(jù)就一覽無余了,所以在偏好設(shè)置里面存儲信息時最好進(jìn)行加密存儲。


有關(guān)偏好設(shè)置加密的一個框架

關(guān)于偏好設(shè)置加密,大家可以用自己的方法進(jìn)行加密,還可以做的就是用第三方加密框架,這里要說的就是NSUserDefaults的一個分類SecureAdditions,大家可以用pod進(jìn)行安裝,如下所示:

pod 'SecureNSUserDefaults'

1. SecureAdditions API

下面我們就看一下該分類或者說框架的API接口。

1. NSUserDefaults+SecureAdditions.h
#import <Foundation/Foundation.h>

@interface NSUserDefaults (SecureAdditions)

- (void)setSecret:(NSString*)secret;

- (BOOL)secretBoolForKey:(NSString *)defaultName;
- (NSData*)secretDataForKey:(NSString *)defaultName;
- (NSDictionary*)secretDictionaryForKey:(NSString *)defaultName;
- (float)secretFloatForKey:(NSString *)defaultName;
- (NSInteger)secretIntegerForKey:(NSString *)defaultName;
- (NSArray *)secretStringArrayForKey:(NSString *)defaultName;
- (NSString *)secretStringForKey:(NSString *)defaultName;
- (double)secretDoubleForKey:(NSString *)defaultName;
- (NSURL*)secretURLForKey:(NSString *)defaultName;
- (id)secretObjectForKey:(NSString *)defaultName;

- (void)setSecretBool:(BOOL)value forKey:(NSString *)defaultName;
- (void)setSecretFloat:(float)value forKey:(NSString *)defaultName;
- (void)setSecretInteger:(NSInteger)value forKey:(NSString *)defaultName;
- (void)setSecretDouble:(double)value forKey:(NSString *)defaultName;
- (void)setSecretURL:(NSURL *)url forKey:(NSString *)defaultName;
- (void)setSecretObject:(id)value forKey:(NSString *)defaultName;

@end
2. NSUserDefaults+SecureAdditions.m
#import "NSUserDefaults+SecureAdditions.h"
#import "CocoaSecurity.h"

#define kStoredObjectKey              @"storedObject"

@implementation NSUserDefaults (SecureAdditions)

static NSString *_secret = nil;

#pragma mark - Getter methods

- (BOOL)secretBoolForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSNumber class]]) {
        return [object boolValue];
    } else {
        return NO;
    }
}

- (NSData*)secretDataForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSData class]]) {
        return object;
    } else {
        return nil;
    }
}

- (NSDictionary*)secretDictionaryForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSDictionary class]]) {
        return object;
    } else {
        return nil;
    }
}

- (float)secretFloatForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSNumber class]]) {
        return [object floatValue];
    } else {
        return 0.f;
    }
}

- (NSInteger)secretIntegerForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSNumber class]]) {
        return [object integerValue];
    } else {
        return 0;
    }
}

- (NSArray *)secretStringArrayForKey:(NSString *)defaultName
{
    id objects = [self secretObjectForKey:defaultName];
    if([objects isKindOfClass:[NSArray class]]) {
        for(id object in objects) {
            if(![object isKindOfClass:[NSString class]]) {
                return nil;
            }
        }
        return objects;
    } else {
        return nil;
    }
}

- (NSString *)secretStringForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSString class]]) {
        return object;
    } else {
        return nil;
    }
}

- (double)secretDoubleForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSNumber class]]) {
        return [object doubleValue];
    } else {
        return 0;
    }
}

- (NSURL*)secretURLForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSURL class]]) {
        return object;
    } else {
        return nil;
    }
}

- (id)secretObjectForKey:(NSString *)defaultName
{
    // Check if we have a (valid) key needed to decrypt
    NSAssert(_secret, @"Secret may not be nil when storing an object securely");
    
    // Fetch data from user defaults
    NSData *data = [self objectForKey:defaultName];
    
    // Check if we have some data to decrypt, return nil if no
    if(data == nil) {
        return nil;
    }
    
    // Try to decrypt data
    @try {
        
        // Generate key and IV
        CocoaSecurityResult *keyData = [CocoaSecurity sha384:_secret];
        NSData *aesKey = [keyData.data subdataWithRange:NSMakeRange(0, 32)];
        NSData *aesIv = [keyData.data subdataWithRange:NSMakeRange(32, 16)];
        
        // Decrypt data
        CocoaSecurityResult *result = [CocoaSecurity aesDecryptWithData:data key:aesKey iv:aesIv];
        
        // Turn data into object and return
        NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:result.data];
        id object = [unarchiver decodeObjectForKey:kStoredObjectKey];
        [unarchiver finishDecoding];
        return object;
    }
    @catch (NSException *exception) {
        
        // Whoops!
        NSLog(@"Cannot receive object from encrypted data storage: %@", exception.reason);
        return nil;
        
    }
    @finally {}
}

#pragma mark - Setter methods

- (void)setSecret:(NSString*)secret
{
    _secret = secret;
}

- (void)setSecretBool:(BOOL)value forKey:(NSString *)defaultName
{
    [self setSecretObject:[NSNumber numberWithBool:value] forKey:defaultName];
}

- (void)setSecretFloat:(float)value forKey:(NSString *)defaultName
{
    [self setSecretObject:[NSNumber numberWithFloat:value] forKey:defaultName];
}

- (void)setSecretInteger:(NSInteger)value forKey:(NSString *)defaultName
{
    [self setSecretObject:[NSNumber numberWithInteger:value] forKey:defaultName];
}

- (void)setSecretDouble:(double)value forKey:(NSString *)defaultName
{
    [self setSecretObject:[NSNumber numberWithDouble:value] forKey:defaultName];
}

- (void)setSecretURL:(NSURL *)url forKey:(NSString *)defaultName
{
    [self setSecretObject:url forKey:defaultName];
}

- (void)setSecretObject:(id)value forKey:(NSString *)defaultName
{
    // Check if we have a (valid) key needed to encrypt
    NSAssert(_secret, @"Secret may not be nil when storing an object securely");
    
    @try {
        
        // Create data object from dictionary
        NSMutableData *data = [[NSMutableData alloc] init];
        NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
        [archiver encodeObject:value forKey:kStoredObjectKey];
        [archiver finishEncoding];
        
        // Generate key and IV
        CocoaSecurityResult *keyData = [CocoaSecurity sha384:_secret];
        NSData *aesKey = [keyData.data subdataWithRange:NSMakeRange(0, 32)];
        NSData *aesIv = [keyData.data subdataWithRange:NSMakeRange(32, 16)];
        
        // Encrypt data
        CocoaSecurityResult *result = [CocoaSecurity aesEncryptWithData:data key:aesKey iv:aesIv];
        
        // Save data in user defaults
        [self setObject:result.data forKey:defaultName];
    }
    @catch (NSException *exception) {
        
        // Whoops!
        NSLog(@"Cannot store object securely: %@", exception.reason);
        
    }
    @finally {}
}

@end

大家看代碼,可以看見里面使用的就是AES對稱加密的方法。


加密框架的使用

下面我就舉一個很小的例子,為大家說一下加密框架的使用。

#import <SecureNSUserDefaults/NSUserDefaults+SecureAdditions.h>
//該類方法的作用就是保存數(shù)據(jù),里面就用到了這個框架

+ (void)saveProfile:(JJUserInfo *)userInfo
{
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setSecret:secretString];
    NSMutableDictionary *userDict = [[NSMutableDictionary alloc] init];
    [userDict setObject:userInfo.userId?:@""       forKey:kUid];
    [userDict setObject:userInfo.token?:@""        forKey:kToken];
    [userDict setObject:userInfo.nickname?:@""     forKey:kUser_nickname];
    [userDict setObject:userInfo.avatar?:@""     forKey:kAvater];
    [userDict setObject:userInfo.coin?:@"" forKey:kCoin];
    [userDict setObject:userInfo.balance?:@"" forKey:kBalance];
    [userDict setObject:userInfo.isArtist?:@"" forKey:kUserType];
    [userDict setObject:userInfo.easePasswordStr?:@"" forKey:kEasePassword];
    [userDefaults setSecretObject:userDict.copy forKey:userData];
    [userDefaults synchronize];
}

后記

關(guān)于APP安全是一個無法完結(jié)的話題,隨著技術(shù)的深入,破解與反破解的技術(shù)進(jìn)行一輪輪的博弈,所以未完,待續(xù)~~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內(nèi)容