NSUserDefaults不安全
NSUserDefaults其實是plist文件中鍵值存儲社牲,并且最大的問題是存在與沙盒中莺丑,這就對安全性埋下了隱患谴返。如果攻擊者破解app,拿到了沙盒中的數(shù)據(jù)命贴,就會造成數(shù)據(jù)泄漏道宅,后果不堪設(shè)想。
當然胸蛛,一般也不會有把密碼直接使用NSUserDefaults存儲的污茵,都會進行加密、或者是多重加密后再進行NSUserDefaults存儲葬项。這么做其實是可行的泞当,前提是加密算法不能泄漏。有個小問題就是玷室,如果用戶刪掉app重裝的話零蓉,之前所有存儲的敏感信息都會消失。比如穷缤,一個用戶誤刪了使用NSUserDefaults存儲密碼的app敌蜂,當重新安裝之后,由于以前是記住密碼免登錄津肛,只因為自己操作不當章喉,接下來要進入找回密碼功能,重新修改密碼才能再次使用app。這對用戶來說是一種相當不友好的體驗秸脱。
Plist不安全
什么是Plist文件落包?
屬性列表(Plist,Property List)是一種結(jié)構(gòu)化的二進制格式文件,包含了內(nèi)嵌鍵值對的可執(zhí)行bundle的基本配置信息摊唇。Plist文件主要用于存儲App的用戶設(shè)置及配置信息咐蝇,例如,游戲類App經(jīng)常會在Plist文件中存儲游戲等級和分數(shù)信息巷查。一般來說有序,App會將存儲用戶數(shù)據(jù)的Plist文件保存在“[App home目錄]/documents/”目錄下。Plist文件可以是XML格式或二進制格式岛请。
Plist文件有哪些安全風險旭寿?
Plist文件主要用于存儲用戶設(shè)置及App的配置信息,但App可能使用Plist文件存儲明文的用戶名崇败、密碼或其它一些個人敏感信息盅称。而保存在Plist文件中的二進制格式文件數(shù)據(jù)則可以使用Plist文件編輯器(如plutil)進行查看或修改,即使在一個沒有越獄的設(shè)備上后室,plist文件也可以通過工具iExplorer獲取缩膝。對于以編碼、未加密或弱加密形式存儲的敏感信息就可能會導致敏感信息泄露了咧擂。
相對比較安全的Keychain
Keychain是iOS所提供的一種安全存儲參數(shù)的方式逞盆,最常用來存儲
賬號
,密碼
松申,用戶信息
,銀行卡資料
等信息俯逾,Keychain會以加密的方式存儲在設(shè)備中
Keychain 的結(jié)構(gòu)
Keychain內(nèi)部可以保存很多的信息贸桶。每條信息作為一個單獨的keychain item,keychain item一般為一個字典桌肴,每條keychain item包含一條data和很多attributes
皇筛。舉個例子,一個用戶賬戶就是一條item坠七,用戶名可以作為一個attribute , 密碼就是data水醋。 keychain雖然是可以保存15000條item,每條50個attributes,但是蘋果工程師建議最好別放那么多彪置,存幾千條密碼拄踪,幾千字節(jié)沒什么問題。
如果把keychain item的類型指定為需要保護的類型比如password或者private key拳魁,item的data會被加密并且保護起來惶桐,如果把類型指定為不需要保護的類型,比如certificates,item的data就不會被加密姚糊。
extern CFTypeRef kSecClassGenericPassword
extern CFTypeRef kSecClassInternetPassword
extern CFTypeRef kSecClassCertificate
extern CFTypeRef kSecClassKey
extern CFTypeRef kSecClassIdentity OSX_AVAILABLE_STARTING(MAC_10_7, __IPHONE_2_0);
Keychain的特點
- 數(shù)據(jù)并不是放在App的Sanbox贿衍,即使刪除了App,資料依然保存在keychain中救恨,如果重新安裝了App贸辈,還可以從keychain中獲取數(shù)據(jù)
- keychain的數(shù)據(jù)可以用group的方式,讓程序可以在App間共享肠槽,不過需要相同的TeamD
- keychain的數(shù)據(jù)是經(jīng)過加密的
Keychain的使用
- 首先導入Security.framework 框架
- Keychain的API提供以下幾個函數(shù)來操作Keychain
- SecItemAdd 添加一個keychain item
- SecItemUpdate 修改一個keychain item
- SecItemCopyMatching 搜索一個keychain item
- SecItemDelete 刪除一個keychain item
根據(jù)特定的Service創(chuàng)建一個用于操作KeyChain的Dictionary
- (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier {
NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
//指定item的類型為GenericPassword
[searchDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
//類型為GenericPassword的信息必須提供以下兩條屬性作為unique identifier
[searchDictionary setObject:encodedIdentifier forKey:(id)kSecAttrAccount]裙椭;
[searchDictionary setObject:encodedIdentifier forKey:(id)kSecAttrService];
return searchDictionary;
}
增
- (BOOL)createKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier {
NSMutableDictionary *dictionary = [self newSearchDictionary:identifier];
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
[dictionary setObject:passwordData forKey:(id)kSecValueData];
OSStatus status = SecItemAdd((CFDictionaryRef)dictionary, NULL);
[dictionary release];
if (status == errSecSuccess) {
return YES;
}
return NO;
}
刪
- (void)deleteKeychainValue:(NSString *)identifier {
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
SecItemDelete((CFDictionaryRef)searchDictionary);
}
改
- (BOOL)updateKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier {
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
[updateDictionary setObject:passwordData forKey:(id)kSecValueData];
OSStatus status = SecItemUpdate((CFDictionaryRef)searchDictionary,
(CFDictionaryRef)updateDictionary);
[searchDictionary release];
[updateDictionary release];
if (status == errSecSuccess) {
return YES;
}
return NO;
}
查
- (BOOL)createKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier {
NSMutableDictionary *dictionary = [self newSearchDictionary:identifier];
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
[dictionary setObject:passwordData forKey:(id)kSecValueData];
OSStatus status = SecItemAdd((CFDictionaryRef)dictionary, NULL);
[dictionary release];
if (status == errSecSuccess) {
return YES;
}
return NO;
}
Keychain API的用法稍微有點復(fù)雜署浩。不過Apple自己也提供了一個封裝了Keychain API的類: KeychainItemWrapper
KeychainItemWrapper是apple官方例子“GenericKeychain”里一個訪問keychain常用操作的封裝類揉燃,在官網(wǎng)上下載了GenericKeychain項目后,只需要把“KeychainItemWrapper.h”和“KeychainItemWrapper.m”拷貝到我們項目筋栋,并導入Security.framework 炊汤。
KeychainItemWrapper的用法
/** 初始化一個保存用戶帳號的KeychainItemWrapper */
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"Account Number"
accessGroup:@"YOUR_APP_ID_HERE.com.yourcompany.AppIdentifier"];
//保存帳號
[wrapper setObject:@"<帳號>" forKey:(id)kSecAttrAccount];
//保存密碼
[wrapper setObject:@"<帳號密碼>" forKey:(id)kSecValueData];
//從keychain里取出帳號密碼
NSString *password = [wrapper objectForKey:(id)kSecValueData];
//清空設(shè)置
[wrapper resetKeychainItem];
其中方法“- (void)setObject:(id)inObject forKey:(id)key;”里參數(shù)“forKey”的值應(yīng)該是Security.framework 里頭文件“SecItem.h”里定義好的key,用其他字符串做key程序會崩潰弊攘!
網(wǎng)上也有許多大神封裝好的框架抢腐,我這里介紹兩個點星上千的框架
SFHFKeychainUtils
SAMKeychain