版本记录
版本号 | 时间 |
---|---|
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安全是一个无法完结的话题,随着技术的深入,破解与反破解的技术进行一轮轮的博弈,所以未完,待续~~~~