一、iOS安全攻防
1.本地數(shù)據(jù)攻防
1.1 文件存儲
每個App的文件都保存在一個沙盒目錄中。每個沙盒都包含Documents、Library/Caches、Tmp、SystemData文件夾。
-Documents: 保存應?運行時生成的需要持久化的數(shù)據(jù),iTunes同步設備時會備份該目錄。
-tmp: 保存應?運行時所需的臨時數(shù)據(jù),使?完畢后再將相應的文件從該目錄刪除。應用沒有運行時,系統(tǒng)也可能會清除該目錄下的文件。iTunes同步設備時不會備份該目錄。
-Library/Caches: 保存應用運行時?成的需要持久化的數(shù)據(jù),iTunes同步設備時不會備份該目錄。一般存儲體積大、不需要備份的非重要數(shù)據(jù),比如網(wǎng)絡數(shù)據(jù)緩存存儲到Caches下。
1.2 數(shù)據(jù)庫
APP一般運用數(shù)據(jù)庫存儲一些數(shù)據(jù)量比較大,邏輯比較復雜的數(shù)據(jù),方便我們使用和增加使用效率。數(shù)據(jù)庫,一般存儲在Documents路徑下的特殊文件,格式一般為.db或者.sqlite,導出后使用特殊工具即可查看如DB Browser for sqlite 和 SQLiteStudio。
防止攻擊者讀取數(shù)據(jù)庫的建議:
數(shù)據(jù)加密: 使用如AES256加密算法對數(shù)據(jù)進行安全加密后再存入數(shù)據(jù)庫中。
優(yōu)點:使用簡單,無需第三方庫支持。
缺點:每次存儲都有加密解密過程,增加APP資源消耗。
整庫加密: 可使用第三方的SQLite擴展庫,對數(shù)據(jù)庫進行整體的加密。如:SQLCipher,git地址,也可以使用pod,在podfile文件中添加:pod 'FMDB/SQLCipher'
1.3 App加固
目前市場上iOS常用的App加固工具有:Obfuscator-LLVM、網(wǎng)易易盾、頂象加固、360加固。以下是幾種加固方式的對比:

1.4 類文件保護
文件保護可以分為靜態(tài)混淆和動態(tài)保護。
- 靜態(tài)混淆:類名、方法名、屬性的混淆
使用 class-dump 導出的頭文件很容易根據(jù)名字知道某個類,方法的作用,這時候需要對類名,方法名進行混淆。第一種方法:通過宏定義混淆,在 pch 文件中比如這樣寫 #define MyClass 89s343ss。網(wǎng)上有一個開源的庫 ios-class-guard。第二種方法:二進制修改,修改可執(zhí)行文件的 __obj_classname 和 objc_methname。也可以通過三方工具codeobscure等對整個項目添加混淆。
- 動態(tài)保護:反調(diào)試、注入檢測、hook 檢測,越獄檢測、簽名檢測等
雖然靜態(tài)混淆后不能猜到類名方法名的作用,但是如果采用動態(tài)的方式還是能知道某些方法的參數(shù)、以及加密后的數(shù)據(jù)等。這就是要防動態(tài)調(diào)試。
- 反調(diào)試:主要通過阻止調(diào)試去附加和檢測調(diào)試器是否存在。主要有 ptrace、sysctl、syscall、ARM 等方式;
- 反反調(diào)試:過濾掉反調(diào)試機制;
- 反注入:檢測當前有沒有其它模塊注入;
動態(tài)注入是利用了 iOS系統(tǒng)中 DYLD_INSERT_LIBRARIES這個環(huán)境變量,如果設置了DYLD_INSERT_LIBRARIES 環(huán)境變量,那么在程序運行時,動態(tài)鏈接器會先加載該環(huán)境變量所指定的動態(tài)庫;也就是說,這個動態(tài)庫的加載優(yōu)先于任何其它的庫,包括 libc。
我們可以在代碼中通過判斷環(huán)境變量來檢測是不是被注入:char *env = getenv("DYLD_INSERT_LIBRARIES");如果方法返回非空,我們可以做一些上報之類的。
- hook 檢測:檢測是否被惡意的 hook 掉了;
當App的二進制格式不會被混淆的前提下,App加載的某個dylib或者編譯時期某個framework的.a文件,其二進制肯定連續(xù)的,意思是在二進制文件中,某個dylib或者framework中符號肯定是相對固定的。譬如,對于上述這個例子,UIViewController這個CLASS的symbol的地址相對于-[UIViewController viewDidLoad]的IMP的地址偏移肯定是固定
- 完整性校驗:load command、代碼校驗、重簽名校驗;
1.5 代碼優(yōu)化
1.5.1UIWebView XSS漏洞
XSS (cross-site script) 跨站腳本自1996年誕生以來,一直被OWASP(open web application security project) 評為十大安全漏洞中的第二威脅漏洞。也有黑客把XSS當做新型的“緩沖區(qū)溢出攻擊”而JavaScript是新型的shellcode。2011年6月份,新浪微博爆發(fā)了XSS蠕蟲攻擊,僅持續(xù)16分鐘,感染用戶近33000個,危害十分嚴重。XSS最大的特點就是能注入惡意的代碼到用戶瀏覽器的網(wǎng)頁上,從而達到劫持用戶會話的目的。由于web應用程序?qū)τ脩舻妮斎脒^濾不嚴產(chǎn)生的。攻擊者利用網(wǎng)站漏把惡意的腳本代碼注入到網(wǎng)頁中,當用戶瀏覽這些網(wǎng)頁時,就會執(zhí)行其中的惡意代碼,對受害用戶可能采用cookie資料竊取,會話劫持,釣魚欺騙等攻擊手段。
解決方案:
(1)對輸入框內(nèi)的數(shù)據(jù)進行關鍵詞過濾(如“script”字符串),避免輸入惡意腳本語言。 (2)在加載網(wǎng)頁的內(nèi)容之前,會用dataWithContentsOfURL方法將加載的網(wǎng)頁的內(nèi)容提取出來放入NSData中,在使用loadHTMLString之前可以對NSData的內(nèi)容過濾,編碼等等,上述代碼中是將script字符串都替換掉,保證了UIWebView不去加載腳本,從而也避免了加載惡意腳本的可能,如下代碼:

或者可以通過這些截斷的URL來做一些白名單、黑名單策略,如禁用file域,不讓它加載本地文件

1.5.2 KVC修改系統(tǒng)值
我們知道,對于類里的私有屬性,Objective-C是無法直接訪問的,但是KVC是可以的。那么對于我們的類方法或者實例方法,用戶可以利用KVC這種方式來改變我們項目里的某個私有變量從而實現(xiàn)篡改代碼的邏輯。
所以我們應該盡量少的暴露方法或成員變量至.h文件中,需要聲明的屬性可以聲明在.m文件中,然后直接使用。
1.6 KeyChain數(shù)據(jù)
Keychain是一個擁有有限訪問權(quán)限的SQLite數(shù)據(jù)庫(AES256加密),可以為多種應用程序或網(wǎng)絡服務存儲少量的敏感數(shù)據(jù)(如用戶名、密碼、加密密鑰等)。如保存身份和密碼,以提供透明的認證,使得不必每次都提示用戶登錄。在iPhone上,Keychain所存儲的數(shù)據(jù)在 /private/var/Keychains/keychain-2.db SQLite數(shù)據(jù)庫中。
數(shù)據(jù)庫內(nèi)數(shù)據(jù),大多數(shù)是加密的,Keychain的數(shù)據(jù)庫內(nèi)容使用了設備唯一的硬件密鑰進行加密,該硬件密鑰無法從設備上導出。因此,存儲在Keychain中的數(shù)據(jù)只能在該臺設備上讀取,而無法復制到另一臺設備上解密后讀取。
一旦攻擊者能夠物理接觸到?jīng)]有設置密碼的iOS設備時,他就可以通過越獄該設備,運行如keychain_dumper這樣的工具,讀取到設備所有的Keychain條目,獲取里面存儲的明文信息。具體方法是通過ssl讓mac連接iPhone,使用keychain_dumper,導出Keychain。
1.7 越獄監(jiān)測
先說判斷設備是否越獄的幾種情況
A.設備越獄普遍都會有以下文件
"/Applications/Cydia.app",
"/Library/MobileSubstrate/MobileSubstrate.dylib",
"/bin/bash",
''/usr/sbin/sshd'',
''/etc/apt''
B.判斷是否存在cydia應用
C.能夠讀取系統(tǒng)所有的應用名稱
非越獄手機是無法獲取系統(tǒng)應用參數(shù)的。
D.讀取環(huán)境變量 DYLD_INSERT_LIBRARIES
非越獄手機DYLD_INSERT_LIBRARIES獲取到的環(huán)境變量為NULL。
結(jié)合以上條件,就可以判斷設備是否越獄,
1.8 防止二次打包
有些hacker可能會通過篡改你的程序包(包括資源文件和二進制代碼)加入一些廣告或則修改你程序的邏輯,然后重新簽名打包,由于第三方hacker獲取不到簽名證書的私鑰,因此會替換掉程序包中簽名相關的文件embedded.mobileprovision,我們可以直接檢查此文件是否被修改,來判斷是否被二次打包,如果程序被篡改,則退出程序。
檢測embedded.mobileprovision是否被篡改:
// 校驗值,可通過上一次打包獲取
#define PROVISION_HASH @"w2vnN9zRdwo0Z0Q4amDuwM2DKhc="
static NSDictionary * rootDic=nil;
void checkSignatureMsg()
{
NSString *newPath=[[NSBundle mainBundle]resourcePath];
if (!rootDic) {
rootDic = [[NSDictionary alloc] initWithContentsOfFile:[newPath stringByAppendingString:@"/_CodeSignature/CodeResources"]];
}
NSDictionary*fileDic = [rootDic objectForKey:@"files2"];
NSDictionary *infoDic = [fileDic objectForKey:@"embedded.mobileprovision"];
NSData *tempData = [infoDic objectForKey:@"hash"];
NSString *hashStr = [tempData base64EncodedStringWithOptions:0];
if (![PROVISION_HASH isEqualToString:hashStr]) {
abort();//退出應用
}
}
2.網(wǎng)絡安全
2.1 Https防護
Https 是一種基于證書的加密連接。 應用服務器首先會向證書頒發(fā)機構(gòu)申請一個數(shù)字證書。這個證書頒發(fā)機構(gòu)一般是一些比較大的組織,比如 Godaddy, Comodo, Symantec 這些公司。
當我們客戶端訪問 https:// 開頭的地址的時候。先會和服務器進行一次 "握手操作"。 服務器會把它的證書返回給客戶端,比如我們電腦上的瀏覽器。
然后客戶端會驗證這個證書是不是可信任的,如果是可信任的才會進行下一步操作。
那么問題來了,什么樣的證書才是可信任的呢, 這就涉及到證書的數(shù)字簽名過程了。 我們前面提到的證書頒發(fā)機構(gòu)都會有一個根證書,根證書上面會有兩個秘鑰,一個公鑰一個私鑰。公鑰是公開的,而私鑰只有頒發(fā)機構(gòu)才會知道。 然后證書頒發(fā)機構(gòu)會用根證書的私鑰為它簽發(fā)的子證書中的整體內(nèi)容進行加密,然后生成數(shù)字簽名,并且包含到這個子證書中,同時子證書中也會包含自己的公鑰和私鑰。 這樣一來,我們在進行 Https 握手的時候,服務器發(fā)送給我們的證書中會帶有數(shù)字簽名, 我們在用頒發(fā)機構(gòu)的公鑰對數(shù)字簽名進行解密,如果解密后的內(nèi)容和證書本身的內(nèi)容能夠完全匹配,那么我們就可以信任這個證書了。
因為數(shù)字簽名只有證書頒發(fā)機構(gòu)用它的私鑰才能夠生成,所以第三方是不能偽造的,除非它能知道頒發(fā)機構(gòu)的私鑰。公鑰-私鑰的這種加密方式,我們稱之為非對稱加密。也就是說加密和解密過程使用的密鑰是不同的。
可以參考下圖:

2.2 接口校驗
使用cycript和反編譯工具是逆向APP工程的基本工具,cycript是解析APP布局和修改代碼邏輯的利器,為防止cycript修改代碼邏輯,從而逃過我們自己代碼的邏輯,我們在編寫重要流程時,需要提醒接口也要鑒權(quán)判斷,如支付訂單時,在錢不夠的情況下,我們不讓button可以點擊,但是通過cycript,我們可以修改代碼button的屬性enable = YES,這樣如果后臺不做判斷導致支付成功,會引發(fā)安全問題。
2.3 防止抓包
通常破解一個 app,我們會抓包。這樣的話,我們的 app 所有接口,接口數(shù)據(jù)都會暴露在逆向人員的眼皮底下。這時候,我們可以限制 http 抓包。方式很簡單,就是將 NSURLSessionConfiguration 的 connection?Proxy?Dictionary 設置成空的字典,因為這個屬性就是用來控制會話的可用代理的。可用參見官方文檔。下面是對于 AFNetWorking 的使用方法:
// 繼承 AFHTTPSessionManager,重寫下列方法
- (instancetype)initWithServerHost:(PDLServerHost*)serverHost {
#ifdef DEBUG
// debug 版本的包仍然能夠正常抓包
self = [super initWithBaseURL:serverHost.baseURL];
#else
// 由于使用 ephemeralSessionConfiguration session 發(fā)起的請求不帶 cookie 和使用緩存等
NSURLSessionConfiguration *conf = [NSURLSessionConfiguration ephemeralSessionConfiguration];
conf.connectionProxyDictionary = @{};
self = [super initWithBaseURL:serverHost.baseURL sessionConfiguration:conf];
#endif
return self;
}
但是由于 OC 方法很容易被 hook,避免抓包是不可能的,所以,最好的方式是對請求參數(shù)進行加密。
2.4 請求參數(shù)加密
為了確保信息的安全,我們采用AES+RSA組合的方式進行接口參數(shù)加密和解密。
AES:對稱加密,加密與解密使用的是同樣的密鑰,所以速度快,但由于需要將密鑰在網(wǎng)絡傳輸,所以安全性不高。
RSA:非對稱加密,使用了一對密鑰,公鑰與私鑰,所以安全性高,但加密與解密速度慢。
所以我們結(jié)合兩者的特性,采用AES對傳輸?shù)臄?shù)據(jù)進行加密,然后采用RSA對AES的秘鑰進行加密。AES的秘鑰是客戶端生成的(iOS我采用的是UUID),然后把AES的秘鑰用不可逆的RSA加密,最后發(fā)送給服務器的是RSA加密過的AES的秘鑰和AES加密的數(shù)據(jù)。服務器端保留RSA的私鑰,可以對AES的秘鑰進行解密,然后用AES的秘鑰解密數(shù)據(jù)。這樣就拿到客戶端的數(shù)據(jù)了。服務器進行數(shù)據(jù)處理過后,用傳過來的AES的秘鑰加密數(shù)據(jù)再傳給客戶端。
3.系統(tǒng)層面安全
3.1 進入App時彈窗驗證
當首次使用一些App時,部分應用會向用戶請求獲得手機位置、通訊錄、日歷或照片的權(quán)限。然而,這些權(quán)限有些是必要的,有些則存在過度收集用戶信息的行為。
判斷App是否“越界獲取”隱私權(quán)限的標準是 App向用戶提供的功能是否必須用到相應的權(quán)限。也就是說,只要不妨礙正常使用App,某些權(quán)限用戶大可不必授權(quán),以此最大程度地保護個人隱私。
以地理位置為例,除非需要使用地圖類App,否則都可以不授權(quán)。如果不需要掃二維碼、錄音、拍照、拍視頻,用戶也無需授權(quán)相機和麥克風。值得注意的是,短信/通訊錄這類權(quán)限,最好都不要授權(quán)。
3.2 剪切板的緩存
我們在使用剪切板進行操作時,iOS系統(tǒng)會緩存所操作的數(shù)據(jù),如果數(shù)據(jù)涉及隱私,將會帶來安全問題。如下圖,iOS的三種剪切板和功能,其中系統(tǒng)級別的剪切板優(yōu)先級最高。

注意:在獲取剪切板內(nèi)容時,總是先取系統(tǒng)的剪切板內(nèi)容,若沒有值才會取應用間的剪切板內(nèi)容。所以在使用完剪切板時,我們在程序退出時,清空剪切板內(nèi)容。

3.3 系統(tǒng)鍵盤緩存
我們打字在使用系統(tǒng)鍵盤打字時時,會出現(xiàn)提示,鍵盤一般會緩存這些提示字符串,緩存在手機的“/private/var/mobile/Library/Keyboard/dynamic-text.dat”文件中,攻擊者可以提取出來,出現(xiàn)頻率最高的往往是賬戶名和密碼。
可以采用應用內(nèi)自定義鍵盤,銀行類APP大多使用自定義鍵盤。
第三方鍵盤權(quán)限問題:
第三方鍵盤可能會收集自己的信息以及上傳自己的信息,因此在蘋果設置里禁止這種行為至關重要,再有就是,因為第三方鍵盤不可控因素很多,可能緩存數(shù)據(jù)邏輯與系統(tǒng)鍵盤不一樣,防止攻擊者攻擊本地緩存數(shù)據(jù),因此建議盡量少用第三方鍵盤。如下圖,蘋果給的建議。
另外,我們使用的第三方鍵盤,會上傳你的鍵入數(shù)據(jù)到服務器,這樣我們的隱私完全暴露給第三方,使用時因注意不要啟用不完全訪問。
3.4 高斯模糊
防止手機截屏圖片泄密方案:
一般我們在應用被掛起時,在當前頁面添加一層高斯模糊,在應用重新進入前臺時,刪除模糊效果,代碼如下:
代碼如下:


4.Xcode來源
從非官方渠道下載的xcode,被導入惡意代碼會上傳產(chǎn)品自身的部分基本信息,包括安裝時間、應用id,應用名稱、系統(tǒng)版本以及國家和語言等。
驗證Xcode來源:
終端內(nèi)輸入:spctl --assess --verbose/Applications/Xcode.app
如果輸出是/Applications/Xcode.app: accepted source=Apple
或者/Applications/Xcode.app: accepted source=Apple System
或者/Applications/Xcode.app: accepted source=Mac App Store
中的一個,表明來源可靠。
因此,我們?yōu)榉乐构ぞ吆偷谌綆毂晃廴?,使用時一定要注意來源,對于SDK來說,選用之前要做到觀看代碼邏輯,避免惡意代碼,對于含有動態(tài)庫或者靜態(tài)庫的SDK,盡量避免使用,或者運用反編譯手段,逆向SDK,這點難度較大。
二、黑客攻擊工具
1.netcat
iTunes Store的數(shù)據(jù)都在/var/mobile/Library/AddressBook/AddressBook.sqlitedb中,只要能能拿出AddressBook.sqlitedb就可以非法拿到用戶的數(shù)據(jù)。
利用netcat,指定服務名稱,就可以抓取設備 iTunesstore 信息。
2.GDB
使用 <sys/types.h> 和 <dlfcn.h> 庫來阻止 GDB(GDB是用于調(diào)試C程序的一種工具,一般包含在gcc工具鏈中)依附,從而達到阻止攻擊者破解代碼后,對我們的程序進行調(diào)試。
3.iOS逆向
3.1 dumpdecrypted
App Store上的應用都使用了FairPlay DRM數(shù)字版權(quán)加密保護技術(shù)。我們要對文件進行反匯編,而IPA都是加密的,哪怎么辦呢?所以在逆向之前我們需要先對應用進行砸殼。這里我們使用的是 dumpdecrypted。(還可以使用Clutch,這里我們就不講Clutch了)
3.2 class-dump
class-dump是用于解析Mach-O文件中存儲的OC運行時信息的。他能生成類的聲明、分類、協(xié)議。和otool -ov類似,但是由于class-dump的結(jié)果是以OC代碼展示的,所以有很強的可讀性。
3.3 Reveal
Reveal能夠在運行時調(diào)試和修改iOS應用程序。它能連接到應用程序,并允許開發(fā)者編輯各種用戶界面參數(shù),
這反過來會立即反應在程序的UI上。就像用FireBug調(diào)試HTML頁面一樣,在不需要重寫代碼、
重新構(gòu)建和重新部署應用程序的情況下就能夠調(diào)試和修改iOS用戶界面。
3.4 Cycript
Cycript是一個理解Objective-C語法的javascript解釋器,這意味著我們能夠在一個命令中用Objective-C或者javascript, 甚至2者兼用。它能夠掛鉤正在運行的進程,能夠在運行時修改應用的很多東西。使用Cycript有如下好處: 1.我們能夠掛鉤正在運行的進程,并且找出正被使用的類信息,例如view controllers,內(nèi)部和第3方庫,甚至程序的delegate的名稱。 2.對于一個特定的類,例如View Controller, App delegate或者任何其他的類,我們能夠得到所有被使用的方法名稱。 3.我們能夠得到所有實例變量的名稱和在程序運行的任意時刻實例變量的值。 4.我們能夠在運行時修改實例變量的值。 5.我們能夠執(zhí)行Method Swizzling,例如替換一個特定方法的實現(xiàn)。 6.我們可以在運行時調(diào)用任意方法,即使這個方法目前并不在應用的實際代碼當中。
3.5 Charles、Wireshark
Charles是Mac下常用的對網(wǎng)絡流量進行分析的工具,類似于Windows下的Fiddler。 在開發(fā)iOS程序的時候,往往需要調(diào)試客戶端和服務器的API接口,這個時候就可以用Charles,Charles能夠攔截SSL請求、模擬慢速網(wǎng)絡、 支持修改網(wǎng)絡請求包并多次發(fā)送、能夠篡改Request和Response等強大的功能。
參考資料:
App 安全方案
iOS 安全攻防
《iOS 應用逆向與安全》讀后感
對 iOS app 進行安全加固
念茜的博客
Objective-C 使用宏定義來做最基本的代碼函數(shù)混淆
iOS移動端的安全架構(gòu)設計