最近工作中完成了項(xiàng)目的用戶信息本地存儲(chǔ)骑歹,查閱了一些本地存儲(chǔ)加密方法等相關(guān)資料叁温。期間發(fā)現(xiàn)了一個(gè)來(lái)自印度理工學(xué)院(IIT)的信息安全工程師的個(gè)人博客回俐,寫了大量有關(guān)iOS Application security的文章镊绪。
個(gè)人感覺寫的還不錯(cuò)夹攒,實(shí)用性比較強(qiáng),加之閱讀難度不大锋八,于是趁著工作日無(wú)聊之際浙于,小翻譯了一篇。
原文IOS Application Security Part 20 – Local Data Storage (NSUserDefaults, CoreData, Sqlite, Plist files)地址http://highaltitudehacks.com/2013/10/26/ios-application-security-part-20-local-data-storage-nsuserdefaults/
真的不難挟纱,還是建議大家閱讀原文羞酗。OK,廢話不多說(shuō)紊服。上譯文:
iOS應(yīng)用安全(20) - 本地?cái)?shù)據(jù)存儲(chǔ)
-2013.10.26
-發(fā)表自Prateek Gianchandani
在這篇文章中檀轨,我們將要看看應(yīng)用中存儲(chǔ)數(shù)據(jù)到本地的一些不同的方法以及這些方法的安全性。
我們將會(huì)在一個(gè)demo上這些這些測(cè)試欺嗤,你可以從我的github賬號(hào)上下載這個(gè)例子程序参萄。對(duì)于CoreData的例子,你可以從這下載例子程序煎饼。本例有一個(gè)不同點(diǎn)就是我們將會(huì)在模擬器上運(yùn)行這些應(yīng)用讹挎,而不是在設(shè)備上運(yùn)行。這樣做的目的是為了證明在前面文章中的操作都可以通過Xcode來(lái)把這些應(yīng)用運(yùn)行在模擬器上吆玖。當(dāng)然筒溃,你也可以使用前面文章中的步驟把這應(yīng)用安裝到設(shè)備上。
NSUserDefaults
保存用戶信息和屬性的一個(gè)非常普通的方法就是使用NSUserDefaults沾乘。保存在NSUserDefaults中的信息在你的應(yīng)用關(guān)閉后再次打開之后依然存在怜奖。保存信息到NSUserDefaults的一個(gè)例子就是保存用戶是否已登錄的狀態(tài)。我們把用戶的登錄狀態(tài)保存到NSUserDefaults以便用戶關(guān)閉應(yīng)用再次打開應(yīng)用的時(shí)候翅阵,應(yīng)用能夠從NSUserDefaults獲取數(shù)據(jù)歪玲,根據(jù)用戶是否登錄展示不同的界面。有些應(yīng)用也用這個(gè)功能來(lái)保存機(jī)密數(shù)據(jù)掷匠,比如用戶的訪問令牌滥崩,以便下次應(yīng)用登錄的時(shí)候,它們能夠使用這個(gè)令牌來(lái)再次認(rèn)證用戶讹语。
從我的github可以下載例子應(yīng)用钙皮,運(yùn)行起來(lái)。你可以得到下面的界面募强,現(xiàn)在輸入一些信息到與NSUserDefaults相關(guān)的文本框株灸,然后點(diǎn)擊下面的“Save in NSUserDefaults”崇摄。這樣數(shù)據(jù)就保存到NSUserDefaults了擎值。
許多人不知道的是保存到NSUserDefaults的數(shù)據(jù)并沒有加密,因此可以很容易的從應(yīng)用的包中看到逐抑。NSUserDefaults被存在一個(gè)以應(yīng)用的bundle id為名稱的plist文件中鸠儿。 首先,我們需要找到我們應(yīng)用的bundle id。因?yàn)槲覀冊(cè)谀M器上運(yùn)行进每,我們可以在/Users/$username/Library/Application Support/iPhone Simulator/$ios version of simulator/Applications/找到應(yīng)用汹粤。我這的路徑是:“Users/prateekgianchandani/Library/Application Support/iPhone Simulator/6.1/Applications”。
一旦我們找到那個(gè)目錄田晚,我們可以看到一堆應(yīng)用嘱兼。我們可以用最近修改的日期找到我們的應(yīng)用,因?yàn)樗亲罱薷牡摹?br>
進(jìn)入到應(yīng)用的bundle里面贤徒。通過NSUserDefaults保存的數(shù)據(jù)都可以在如下圖所示的Library -> Preferences -> $AppBundleId.plist文件中找到芹壕。
打開這個(gè)plist文件,我們可以清楚的看到這個(gè)文件的內(nèi)容接奈。
有時(shí)候踢涌,plist文件會(huì)以二進(jìn)制格式保存,因此可能第一下看到會(huì)覺得不可讀序宦。你可以用plutil工具把它轉(zhuǎn)成xml格式睁壁,或者直接用iExplorer在設(shè)備上查看。
Plist 文件
另一種保存數(shù)據(jù)普遍用的方法就是plist文件互捌。Plist文件應(yīng)該始終被用來(lái)保存那些非機(jī)密的文件潘明,因?yàn)樗鼈儧]有加密,因此即使在非越獄的設(shè)備上也非常容易被獲取疫剃。已經(jīng)有漏洞被爆出來(lái)钉疫,大公司把機(jī)密數(shù)據(jù)比如訪問令牌,用戶名和密碼保存到plist文件中巢价。在下面的demo中牲阁,我們輸入一些信息并保存到plist文件。
下面是把數(shù)據(jù)保存到plist文件的代碼壤躲。
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingString:@"/userInfo.plist"];
NSMutableDictionary* plist = [[NSMutableDictionary alloc] init];[plist setValue:self.usernameTextField.text forKey:@"username"];[plist setValue:self.passwordTextField.text forKey:@"passwprd"];[plist writeToFile:filePath atomically:YES];
如你所見城菊,我們能夠給plist文件指定路徑。我們可以搜索整個(gè)應(yīng)用的所有plist文件碉克。在這里凌唬,我們找到一個(gè)叫做userinfo.plist的文件。
可以看到漏麦,它包含了我們剛剛輸入的用戶名/密碼的組合客税。
CoreData和Sqlite文件
因?yàn)镃oreData內(nèi)部使用Sqlite來(lái)保存信息,因此我們這里將只會(huì)介紹下CoreData撕贞。如果你不知道什么是CoreData更耻,下面是從蘋果文檔介紹CoreData截的圖。
因此捏膨,基本上秧均,CoreData可以用來(lái)創(chuàng)建一個(gè)model食侮,管理不同對(duì)象的關(guān)系,把數(shù)據(jù)保存到本地目胡,然后當(dāng)你查詢的時(shí)候從本地緩存中獲取它們锯七。本例中,我們將使用一個(gè)demo誉己,位于github眉尸。運(yùn)行起來(lái),你會(huì)發(fā)現(xiàn)它只是一個(gè)簡(jiǎn)單的RSS feed巨双。
這個(gè)應(yīng)用用CoreData保存數(shù)據(jù)效五。一個(gè)非常重要的一點(diǎn)就是CoreData內(nèi)部使用sql,因此所有文件都以.db文件保存炉峰。我們到這個(gè)app的bundle中去看看畏妖。 在這個(gè)app的bundle中,你可以看到那里有一個(gè)MyCoreData.sqlite的文件疼阔。
我們可以用sqlite3分析戒劫。我這slite文件的地址是:~/Library/Application Support/iPhone Simulator/6.1/Applications/51038055-3CEC-4D90-98B8-A70BF12C7E9D/Documents.
我們可以看到,這里有個(gè)叫做ZSTORIES的表婆廊。在Core Data中迅细,每個(gè)表名開頭都會(huì)被追加一個(gè)Z。這意味著真正的實(shí)體名稱是STORIES淘邻,如我們?cè)诠こ痰脑创a文件看到的那樣茵典。
我們可以非常容易的導(dǎo)出這個(gè)表的所有值。請(qǐng)卻表headers的狀態(tài)是on宾舅。
正如我們看到的那樣统阿,默認(rèn)的,保存在CoreData的數(shù)據(jù)都是沒有加密的筹我,因此可以輕易的被取出扶平。因此,我們不應(yīng)該用 CoreData保存機(jī)密數(shù)據(jù)蔬蕊。 有些庫(kù)包裝了一下CoreData, 聲稱能夠保存加密數(shù)據(jù)结澄。也有些庫(kù)能夠把數(shù)據(jù)加密保存到設(shè)備上,不過不使用CoreData岸夯。例如麻献,Salesforce Mobile SDK 就使用了一個(gè)被稱為SmartStore的功能來(lái)把加密數(shù)據(jù)以"Soups"的形式保存到設(shè)備上。
Keychain
有些開發(fā)者不太喜歡把數(shù)據(jù)保存到Keychain中猜扮,因?yàn)閷?shí)現(xiàn)起來(lái)不那么直觀勉吻。不過,把信息保存到Keychain中可能是非越獄設(shè)備上最安全的一種保存數(shù)據(jù)的方式了破镰。而在越獄設(shè)備上餐曼,沒有任何事情是安全的。這篇文章展示了使用一個(gè)簡(jiǎn)單的wrapper類鲜漩,把數(shù)據(jù)保存到keychain是多么的簡(jiǎn)單源譬。使用這個(gè)wrapper來(lái)保存數(shù)據(jù)到keychain就像把數(shù)據(jù)保存到NSUserDefaults那么簡(jiǎn)單。下面就是一段把字符串保存到keychain的代碼孕似。請(qǐng)注意和使用NSUserDefaults的語(yǔ)法非常類似踩娘。
PDKeychainBindings *bindings = [PDKeychainBindings sharedKeychainBindings];
[bindings setObject:@"XYZ" forKey:@"authToken"];
下面是一段從keychain中取數(shù)據(jù)的代碼。
PDKeychainBindings *bindings = [PDKeychainBindings sharedKeychainBindings];
NSLog(@"Auth token is %@",[bindings objectForKey:@"authToken"]]);
一些小技巧
正如之前討論過的那樣喉祭,沒有任何信息在越獄設(shè)備上是安全的养渴。攻擊者能夠拿到Plist文件,導(dǎo)出整個(gè)keychain泛烙,替換方法實(shí)現(xiàn)理卑,并且攻擊者能做他想做的任何事情。不過開發(fā)者能夠使用一些小技巧來(lái)使得腳本小子從應(yīng)用獲得信息變得更難蔽氨。比如把文件加密放到本地設(shè)備上藐唠。這里這篇文章詳細(xì)的討論了這一點(diǎn)○木浚或者你可以使得攻擊者更難理解你的信息宇立。比如考慮要把某個(gè)用戶的認(rèn)證令牌(authentication token)保存到keychain當(dāng)中,腳本小子可能就會(huì)導(dǎo)出keychain中的這個(gè)數(shù)據(jù)自赔,然后試圖劫持用戶的會(huì)話妈嘹。我們只需再把這個(gè)認(rèn)證令牌字符串反轉(zhuǎn)一下(reverse),然后再保存到keychain中绍妨,那么攻擊者就不太可能會(huì)知道認(rèn)證令牌是反轉(zhuǎn)保存的润脸。當(dāng)然,攻擊者可以追蹤你的應(yīng)用的每一個(gè)調(diào)用他去,然后理解到這一點(diǎn)津函,但是,一個(gè)如此簡(jiǎn)單的技術(shù)就能夠讓腳本小子猜足夠的時(shí)間孤页,以至于他們會(huì)開始尋找其它應(yīng)用的漏洞尔苦。另一個(gè)簡(jiǎn)單技巧就是在每個(gè)真正的值保存之前都追加一個(gè)常量字符串。
在接下來(lái)的文章里行施,我們將討論使用GDB進(jìn)行運(yùn)行時(shí)分析。
譯 者:MysticCoder
出 處:http://www.cnblogs.com/mysticCoder/
關(guān)于作者:專注于iOS項(xiàng)目開發(fā)蛾号。如有問題或建議,請(qǐng)多多賜教展运!
版權(quán)聲明:本文版權(quán)歸作者和博客園共有活逆,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明拗胜,且在文章頁(yè)面明顯位置給出原文鏈接。
特此聲明:所有評(píng)論和私信都會(huì)在第一時(shí)間回復(fù)埂软。也歡迎園子的大大們指正錯(cuò)誤,共同進(jìn)步勘畔∷模或者直接私信我
聲援博主:如果您覺得文章對(duì)您有幫助,可以點(diǎn)擊文章右下角【推薦】一下炫七。您的鼓勵(lì)是作者堅(jiān)持原創(chuàng)和持續(xù)寫作的最大動(dòng)力!