APP安全机制(八)—— 偏好设置的加密存储

版本记录

版本号 时间
V1.0 2017.08.29

前言

在这个信息爆炸的年代,特别是一些敏感的行业,比如金融业和银行卡相关等等,这都对app的安全机制有更高的需求,很多大公司都有安全 部门,用于检测自己产品的安全性,但是及时是这样,安全问题仍然被不断曝出,接下来几篇我们主要说一下app的安全机制。感兴趣的看我上面几篇。
1. APP安全机制(一)—— 几种和安全性有关的情况
2. APP安全机制(二)—— 使用Reveal查看任意APP的UI
3. APP安全机制(三)—— Base64加密
4. APP安全机制(四)—— MD5加密
5. APP安全机制(五)—— 对称加密
6. APP安全机制(六)—— 非对称加密
7. APP安全机制(七)—— SHA加密

偏好设置存储信息的隐患

大家都知道,存储可以有很多种方式,其中一种就是偏好设置进行存储,但是在存储之前如果不进行任何加密的话,那么黑客只要是将沙盒攻陷获取里面的信息,你的数据就裸奔了,你的敏感数据就一览无余了,所以在偏好设置里面存储信息时最好进行加密存储。


有关偏好设置加密的一个框架

关于偏好设置加密,大家可以用自己的方法进行加密,还可以做的就是用第三方加密框架,这里要说的就是NSUserDefaults的一个分类SecureAdditions,大家可以用pod进行安装,如下所示:

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>
//该类方法的作用就是保存数据,里面就用到了这个框架

+ (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];
}

后记

关于APP安全是一个无法完结的话题,随着技术的深入,破解与反破解的技术进行一轮轮的博弈,所以未完,待续~~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • 原文: http://mrpeak.cn/blog/encrypt/ 移动端App安全如果按CS结构来划分的话,主...
    vb12阅读 844评论 0 6
  • 烟水孤舟,渔樵垂钓。 我笑你痴,你笑我傲。 水底鱼儿也在笑, 笑什么? 我也不知道。 来来来, 一只竹笛,一曲小调...
    谌心阅读 267评论 0 0
  • 标题:Doing his darnedest Doing one's darnedest: 全力以赴to put ...
    LizhuHuang阅读 1,166评论 0 0
  • 大家好,这里是100字说成长 这里有最实用的干货分享,这里有最简短的方法论沉淀,100字说成长,用短短100字陈述...
    100字说成长阅读 656评论 0 3