根據蘋果的介紹,iOS設備中的Keychain是一個安全的存儲容器,可以用來為不同應用保存敏感信息比如用戶名,密碼,網絡密碼,認證令牌。蘋果自己用keychain來保存Wi-Fi網絡密碼,VPN憑證等等。它是一個sqlite數據庫,位於/private/var/Keychains/keychain-2.db,其保存的所有數據都是加密過的。
開發者通常會希望能夠利用操作系統提供的功能來保存憑證(credentials)而不是把它們(憑證)保存到NSUserDefaults,plist文件等地方。保存這些數據的原因是開發者不想用戶每次都要登錄,因此會把認證信息保存到設備上的某個地方並且在用戶再次打開應用的時候用這些數據自動登錄。Keychain的信息是存在於每個應用(app)的沙盒之外的。
通過keychain access groups可以在應用之間共享keychain中的數據。要求在保存數據到keychain的時候指定group。把數據保存到keychain的最好方法就是用蘋果提供的KeychainItemWrapper。可以到這下載例子工程。第一步就是創建這個類的實例。
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@”Password” accessGroup:nil];
標識符(Identifier)在後面我們要從keychain中取數據的時候會用到。如果你想要在應用之間共享信息,那麼你需要指定訪問組(access group)。有同樣的訪問組 的應用能夠訪問同樣的keychain信息。
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@”Account Number” accessGroup:@”YOURAPPID_HERE.com.yourcompany.GenericKeychainSuite”];
要把信息保存到keychain中,使用 setObject:forKey: 方法。在這裡, (id)kSecAttrAccount 是一個預先定義好的鍵(key),我們可以用它來保存賬號名稱。 kSecClass指定了我們要保存的某類信息,在這裡是一個通用的密碼。kSecValueData可以被用來保存任意的數據,在這裡是一個密碼。
[keychainItemWrapper setObject:kSecClassGenericPassword forKey:(id)kSecClass];
[wrapper setObject:@"username" forKey:(id)kSecAttrAccount];
[keychainItemWrapper setObject:@"password"forKey:(id)kSecValueData];
[wrapper setObject:(id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(id)kSecAttrAccessible];
kSecAttrAccessiblein變量用來指定這個應用合適需要訪問這個數據。我們需要對這個選項特別注意,並且使用最嚴格的選項。這個鍵(key)可以設置6種值。
你可以從如下對蘋果的文檔的截圖看到。
當然,我們應該絕對不要使用kSecAttrAccessibleAlways。一個安全點的選項是kSecAttrAccessibleWhenUnlocked。這也有些選項是以 ThisDeviceOnly 結尾的。如果選中了這個選項,那麼數據就會被以硬件相關的密鑰(key)加密,因此不能被傳輸到或者被其他設備看到。即使它們提供了進一步的安全性,使用它們可能不是一個好主意,除非你有 一個更好的理由不允許數據在備份之間遷移。
要從keychain中獲取數據,可以用 NSString *accountName = [wrapper objectForKey:(id)kSecAttrAccount];
從Keychain中導出數據的最流行工具是ptoomey3的Keychain dumper。其github地址位於此。現在到這個地址把它下載下來。然後解壓zip文件。在解壓的文件夾內,我們感興趣的文件是keychaindumper這個二進制文件。一個應用能夠訪問的keychain數據是通過其entitlements文件指定的。keychaindumper使用一個自簽名文件,帶有一個*通配符的entitlments,因此它能夠訪問keychain中的所有條目。 當然,也有其他方法來使得所有keychain信息都被授權,比如用一個包含所有訪問組(access group)的entitlements文件,或者使用一個特定的訪問組(access group)使得能夠訪問所有的keychain數據。 例如,工具Keychain-viewer就使用如下的entitlements.
com.apple.keystore.access-keychain-keys
com.apple.keystore.device
現在把這個二進制文件上傳到你的設備的/tmp文件夾,確保它可執行。
現在請確保保存在/private/var/Keychains/keychain-2.db的keychain文件可以被讀取。
現在,運行這個可執行文件
如你所見,它可以導出所有的keychain信息。你可以看到許多保存在這裡的用戶名和密碼。例如,你可以看到Mail應用把你賬號的用戶名和密碼保存在keychain中。 類似的,你也可以找到你之前連接過的無線網絡的密碼和其他更多的信息。上述命令默認只會導出通用和網絡密碼。你可以通過-h命令查看它的用法。
你可以通過 “-a” 命令導出所有數據
使得keychain中的數據更安全的一個做法就是使用一個更強的口令。這是因為對某些特定的保護屬性(protection attributes)來說,口令會被用作加密keychain中的數據。 iOS默認允許4位數字口令(從0-9999),因此很容易被人在幾分鐘之內暴力破解。本系列的後續文章中我們會看看暴力破解口令。設置字母加數字的密碼會讓破解花更多的時間。
一個合適的保護屬性(protection attributes)和口令的組合會讓keychain的數據更難被獲取。
在本文中,我們看到了從iOS設備的Keychain中導出數據是多麼的容易。雖然在keychain中保存憑證(credentials)和敏感信息比NSUserDefaults和plist文件更安全,但是, 想要破解它也不難。
References
Keychain-Dumper
https://github.com/ptoomey3/Keychain-Dumper
本文原文是 IOS Application Security Part 12 – Dumping Keychain Data
注:之前我寫過一篇文章,Keychain is not safe,大家可以對照著看看,有的應用還是在keychain中保存密碼。雖然本文說keychain也容易被破解,不過比NSUserDefaults和plist安全得多,只要我們注意不要在keychain中保存明密碼就會在很大程度上提升安全性。
一般來說mobile app都需要在本地保存一些較為敏感的數據。如何安全的保存這些數據就是一個值得深入探討的問題。
Mac OS 可以利用Keychain保存各應用中用戶的賬號密碼,讓用戶不用重復輸入,在iOS中也有Keychain,也可以在應用之間共享數據,只是有些限制,用戶無法通過手動控制。
要在社保上 KeyChain中的所有數據都以key-value的形式進行存儲,可以對其進行add、update、get、delete操作。
如果需要在應用裡使用keyChain,需要導入Security.framework,keychain的操作接口聲明在頭文件SecItem.h裡。直接使用SecItem.h裡方法操作keychain,需要寫的代碼較為復雜,可以使用已經封裝好了的工具類SFHFKeychainUtils,見:https://github.com/ldandersen/scifihifi-iphone/tree/master/security
對每個應用來說,Keychain都有兩個訪問區,私有區和公共區。私有區是一個sandbox,本程序存儲的任何數據都對其他程序不可見。Keychain中保存的信息是用app unique簽名了的,默認只有自己能夠訪問。
keychain的access group的概念。
a)app保持的信息是用一個app unique 簽名了的,默認只有自己能夠訪問。
" Each application on an iOS device has a unique “application-identifier” that is cryptographically signed into the application before being submitted to the Apple App store. The keychain service restricts which data an application can access based on this identifier. By default, applications can only access data associated with their own application-identifier。By default, when no access group is specified, the application will use the unique application-identifier as the access group (thus limiting access to the application itself)"
b)不同app之間可以通過access_group共享
app1的group是 app1.accessgroup.item1,
app2在entitlements中加入這個item就可以訪問了。
c) 在不同app之間共享,只能在同一個公司內部的app共享。
因為keychain access group 所在的文件entitlements.plist,需要添加到codesignentitlements.
這個文件的路徑要配置在 Project->build setting->Code Signing Entitlements裡,否則公共區無效,配置好後,須用你正式的證書簽名編譯才可通過,否則xcode會彈框告訴你code signing有問題。所以,蘋果限制了你只能同公司的產品共享KeyChain數據,別的公司訪問不了你公司產品的KeyChain。
很多app都這樣保存用戶密碼,可是,這樣就安全了嗎?
對於沒有越獄的設備,上述做法確實很安全。但是,對於jail break之後的設備,風險就很大了。
通過上面的分析,我們知道要訪問keychain裡面的數據,需要
1)和app同樣的證書
jail break 相當於對蘋果做簽名檢查的地方打了個補丁,使得不是蘋果頒發的證書簽名的文件,甚至偽造的簽名文件簽名的app也能正常使用。所以這個可以輕松繞過。
2)獲得access group的名稱
Keychain Dumper Updated for iOS 5 介紹了如何獲得acess group。
其實也可以不必獲得access group,因為access的匹配是可以用正則表達式的,也就是用*就可以匹配所有group了。 例子如下:
<code class=" hljs xml"> <span class="hljs-tag"><<span class="hljs-title">dict</span>></span> <span class="hljs-tag"><<span class="hljs-title">key</span>></span>keychain-access-groups<span class="hljs-tag"></<span class="hljs-title">key</span>></span> <span class="hljs-tag"><<span class="hljs-title">array</span>></span> <span class="hljs-tag"><<span class="hljs-title">string</span>></span>*<span class="hljs-tag"></<span class="hljs-title">string</span>></span> <span class="hljs-tag"></<span class="hljs-title">array</span>></span> <span class="hljs-tag"></<span class="hljs-title">dict</span>></span><span class="hljs-tag"></<span class="hljs-title">plist</span>></span> </code>
3)在設備上執行2)中介紹的keychain dumper,就可以得到所有的相關信息。
但是,要在設備上執行keychain dumper,就需要用chmod 777設置其權限,需要root權限,而jail break之後的默認密碼是:alpine。
最後可以獲得好幾個文件,下面是裡面的2個例子。(密碼都被我用password字串替換)
a) 是家裡的WIFI信息
b)是某知名微博
我在越獄之後的iOS 5.1的iPhone,iPad, iOS 6.1.2的iPad上都測試過,都可以獲得如上信息。 實際中的例子遠不止這2個。很多應用都是直接存用戶的明文密碼的。
a)修改root的默認密碼。
b) 安裝能信任的jail break app。
不要保存用戶的明文密碼。
Encryption is a must for sensitive data。