为啥直接把密码存储在NSUserDefault中不安全?
iOS中的沙盒
默认情况下,每个沙盒都含有3个文件夹:Documents,library,tmp。因为应用的沙盒机制,应用只能在几个目录下读写文件。
- Documents:苹果建议将程序中建立的或在程序中浏览到的文件数据保存在该目录下,iTunes备份和恢复的时候会包括此目录
- Library:存储程序的默认设置或其它状态信息
- Library/Caches:存放缓存文件,iTunes不会备份此目录,此目录下文件不会在应用退出删除
- tmp:提供一个即时创建临时文件的地方
获取沙盒Library路径
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)lastObject];
NSUserDefaults对应的plist文件就在Library的Preferences文件夹下,如果sandbox被破解,或者手机被越狱,那么就能轻松拿到这个plist文件并读取到存储的信息,这样密码就会不安全。所以对于存放在USUserDefaults中的东西,最好混淆加密一下再存储。
如何解决"直接把密码存储在NSUserDefaults中不安全"的问题?
把密码加密后再存储到NSUserDefaults中,比如采用MD5加密,此处不做详细介绍。
不适用NSUserDefaults保存密码,使用keychain来保存密码
优点:
1. 更安全
2. 在删除应用后,密码不会删除,下载安装还能使用
封装的代码:(使用时需要先导入Security.framework)
PassWordTool.h
#import <Foundation/Foundation.h>
@interface PassWordTool : NSObject
/**
* @brief 存储密码
*
* @param password 密码内容
*/
+(void)savePassWord:(NSString *)password;
/**
* @brief 读取密码
*
* @return 密码内容
*/
+(id)readPassWord;
/**
* @brief 删除密码数据
*/
+(void)deletePassWord;
@end
PassWordTool.m
import "PassWordTool.h"
import "KeychainTool.h"
@implementation PassWordTool
static NSString * const KEY_IN_KEYCHAIN = @"com.chenyuan.app.userid";
static NSString * const KEY_PASSWORD = @"com.chenyuan.app.password";
+(void)savePassWord:(NSString *)password
{
NSMutableDictionary *usernamepasswordKVPairs = [NSMutableDictionary dictionary];
[usernamepasswordKVPairs setObject:password forKey:KEY_PASSWORD];
[KeychainTool save:KEY_IN_KEYCHAIN data:usernamepasswordKVPairs];
}
+(id)readPassWord
{
NSMutableDictionary *usernamepasswordKVPair = (NSMutableDictionary *)[KeychainTool load:KEY_IN_KEYCHAIN];
return [usernamepasswordKVPair objectForKey:KEY_PASSWORD];
}
+(void)deletePassWord
{
[KeychainTool delete:KEY_IN_KEYCHAIN];
}
@end
KeyChainTool.h
#import <Foundation/Foundation.h>
@interface KeychainTool : NSObject
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service ;
+ (void)save:(NSString *)service data:(id)data ;
+ (id)load:(NSString *)service ;
+ (void)delete:(NSString *)service ;
@end
KeychainTool.m
@implementation KeychainTool
+(NSMutableDictionary *)getKeychainQuery:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge_transfer id)kSecClassGenericPassword,(__bridge_transfer id)kSecClass,
service, (__bridge_transfer id)kSecAttrService,
service, (__bridge_transfer id)kSecAttrAccount,
(__bridge_transfer id)kSecAttrAccessibleAfterFirstUnlock,(__bridge_transfer id)kSecAttrAccessible,
nil];
}
+(void)save:(NSString *)service data:(id)data {
//Get search dictionary
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Delete old item before add new item
SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
//Add new object to search dictionary(Attention:the data format)
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data
] forKey:(__bridge_transfer id)kSecValueData];
//Add item to keychain with the search dictionary
SecItemAdd((__bridge_retained CFDictionaryRef)keychainQuery, NULL);
}
+(id)load:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Configure the search setting
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge_transfer id)kSecReturnData];
[keychainQuery setObject:(__bridge_transfer id)kSecMatchLimitOne forKey:(__bridge_transfer id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((__bridge_retained CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
@try {
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge_transfer NSData *)keyData];
} @catch (NSException *e) {
NSLog(@"Unarchive of %@ failed: %@", service, e);
} @finally {
}
}
return ret;
}
+(void)delete:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
}
@end
使用keychain保存密码的另一种方式:
http://blog.csdn.net/cloudox_/article/details/48248651
网友评论