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安全是一個無法完結的話題,隨著技術的深入,破解與反破解的技術進行一輪輪的博弈,所以未完,待續~~~~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 原文: http://mrpeak.cn/blog/encrypt/ 移動端App安全如果按CS結構來劃分的話,主...
    vb12閱讀 919評論 0 6
  • 煙水孤舟,漁樵垂釣。 我笑你癡,你笑我傲。 水底魚兒也在笑, 笑什么? 我也不知道。 來來來, 一只竹笛,一曲小調...
    諶心閱讀 339評論 0 0
  • 標題:Doing his darnedest Doing one's darnedest: 全力以赴to put ...
    LizhuHuang閱讀 1,261評論 0 0
  • 大家好,這里是100字說成長 這里有最實用的干貨分享,這里有最簡短的方法論沉淀,100字說成長,用短短100字陳述...
    100字說成長閱讀 706評論 0 3