为什么直接把密码存储在NSUserDefaults中不安全?
iOS中沙盒有哪几个文件夹,都是用来干吗的
获取到沙盒Library路径//获取Library目录路径 - (void)getLibraryPath { NSArray * pathArray = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask, YES); NSString * libraryStrPath = [pathArray objectAtIndex:0]; NSLog(@"LibraryPath:%@“,libraryStrPath); } 如图就是NSUserDefaults对应的plist文件在sandbox中的位置
蓝色部分为plist文件
如果sandbox被破解,或者你的手机被越狱,那么就能轻松拿到这个文件。
在plist中可以找到密码
这对于其它保存在NSUserDefaults中的信息也是一样的,所以对于存在NSUserDefaults中的东西,最好混淆加密一下再存储。 如何删除NSUserDefaults对应的plist文件?其实就是删除plist文件中所有的键值对。 NSUserDefaults *userDefatluts = [NSUserDefaults standardUserDefaults]; NSDictionary *dictionary = [userDefaults dictionaryRepresentation]; for(NSString* key in [dictionary allKeys]){ [userDefaults removeObjectForKey:key]; [userDefaults synchronize]; }
如何解决“直接把密码存储在NSUserDefaults中不安全”的问题?把密码加密后再存储到NSUserDefaults中
iOS中提供了很多种加密算法,对于存储密码,可以使用不可逆的MD5加密。 ##### 简单的MD5加密 + ( NSString *)md5String:( NSString *)str { const char *myPasswd = [str UTF8String ]; unsigned char mdc[ 16 ]; CC_MD5 (myPasswd, ( CC_LONG ) strlen (myPasswd), mdc); NSMutableString *md5String = [ NSMutableString string ]; for ( int i = 0 ; i< 16 ; i++) { [md5String appendFormat : @"%02x" ,mdc[i]]; } return md5String; } ##### 复杂一些的MD5加密 + ( NSString *)md5String:( NSString *)str { const char *myPasswd = [str UTF8String ]; unsigned char mdc[ 16 ]; CC_MD5 (myPasswd, ( CC_LONG ) strlen (myPasswd), mdc); NSMutableString *md5String = [ NSMutableString string ]; [md5String appendFormat : @"%02x" ,mdc[ 0 ]]; for ( int i = 1 ; i< 16 ; i++) { [md5String appendFormat : @"%02x" ,mdc[i]^mdc[ 0 ]]; 不使用NSUserDefaults保存密码,使用keyChain来保存密码更加保险的方法是把密码保存在iOS提供的keychina中,并且删除应用后,密码不会删除,下载安装还能使用。iOS系统提供了一些方法,进行一些简单的封装之后,就可以很方便的使用。
封装后可供使用的几个类
下面是封装代码,使用时需要先导入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 ``` # import "KeychainTool.h" @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 服务器密码验证登录请求
验证请求时,最好是不直接把明文密码包含在请求里面。 NSString *sourceStr = [NSString stringWithFormat:@"attach=iOS&chartset=utf-8&format=json&partner=google&userid=%@&password=%@”,userid,password]; NSString *signStr = [NSString md5String:sourceStr]; 这样得到的signStr和userid再作为网络请求的参数传给服务器做验证。 (责任编辑:好模板) |