這篇文章主要介紹了Mobile BI(移動(dòng)商務(wù)智能)使用過(guò)程中本地的數(shù)據(jù)安全,主要包括如何保護(hù)以及數(shù)據(jù)加密兩塊。
文章的具體內(nèi)容包括:數(shù)據(jù)格式(Binary Data、XML、JSON),數(shù)據(jù)保護(hù)(Cache Protection、Confidential Project、DPC),數(shù)據(jù)加密(文件加密、憑證加密)、iOS Coding、引用。
數(shù)據(jù)格式
在移動(dòng)應(yīng)用中,數(shù)據(jù)的格式保證了各模塊之間數(shù)據(jù)交互的可能性。這包括了移動(dòng)應(yīng)用和服務(wù)器之間、移動(dòng)應(yīng)用自身各模塊之間、以及移動(dòng)應(yīng)用與移動(dòng)存儲(chǔ)之間的數(shù)據(jù)交互,一般而言,這三種類型都會(huì)使用相同的數(shù)據(jù)格式。
對(duì)于我們的Mobile BI產(chǎn)品,iOS主要基于Binary Data,而Android類似于Web,主要使用了JSON Data,另外XML也在一部分功能中被使用,比如配置文件等。
Binary Data
這屬于自定義的一種數(shù)據(jù)格式,將數(shù)據(jù)的信息序列化到二進(jìn)制數(shù)據(jù)上,而為了服務(wù)器和客戶端能夠交流,客戶端會(huì)使用服務(wù)器相同的邏輯來(lái)處理二進(jìn)制數(shù)據(jù)的信息。
因?yàn)閿?shù)據(jù)的格式是自定義的,所以相應(yīng)的不需要太多上下文的信息,也更容易進(jìn)行優(yōu)化,所以相比XML或者JSON而言,傳輸效率是最高的。
XML
XML Extensible Markup Language 可擴(kuò)展標(biāo)記語(yǔ)言,被設(shè)計(jì)用來(lái)傳送及攜帶數(shù)據(jù)信息,而不用來(lái)表現(xiàn)或展示數(shù)據(jù),與此對(duì)應(yīng)的HTML語(yǔ)言是用來(lái)表現(xiàn)數(shù)據(jù)的。需要學(xué)習(xí)XML語(yǔ)法規(guī)范的可以參見(jiàn)w3schools XML Tutorial。
示例
<?xml version="1.0" encoding="UTF-8"?>
<breakfast_menu>
<food>
<name>Belgian Waffles</name>
<price>$5.95</price>
<description>
Two of our famous Belgian Waffles with plenty of real maple syrup
</description>
<calories>650</calories>
</food>
<food>
<name>Strawberry Belgian Waffles</name>
<price>$7.95</price>
<description>
Light Belgian waffles covered with strawberries and whipped cream
</description>
<calories>900</calories>
</food>
</breakfast_menu>
XML標(biāo)簽是大小寫(xiě)敏感的,此外,因?yàn)閄ML格式本身需要使用一些特殊字符,為了避免這些字符在作為值時(shí)與XML本身的格式信息沖突,我們需要進(jìn)行一些格式轉(zhuǎn)換,即Escaping Characters: < -> <, > -> >, & -> &, ' -> ' and " -> "
在使用XML的過(guò)程中,可以將葉節(jié)點(diǎn)值存儲(chǔ)在屬性Attribute或者元素Element中,即 ANF Attribute Normal Form <ml v="250"/>, 以及 ENF Element Normal Form <ml><v>250</v></ml>
DOM Document Object Model 文檔對(duì)象模型,為XML的標(biāo)準(zhǔn)編程接口。DOM把XML文檔作為樹(shù)結(jié)構(gòu)來(lái)查看,通常需要加載整個(gè)文檔和構(gòu)造層次結(jié)構(gòu)來(lái)進(jìn)行操作。因?yàn)檎麄€(gè)樹(shù)被加載在內(nèi)存中,所以相應(yīng)的數(shù)據(jù)結(jié)構(gòu)的修改、查找會(huì)方面很多。
SAX Simple API for XML 是一個(gè)循序存取XML的解析器API,SAX運(yùn)行時(shí)是單向的,即解析過(guò)的資料無(wú)法在不重新開(kāi)始的情況下再次讀取。相比較DOM,SAX僅需要很少量的內(nèi)存,DOM一般需要將整棵樹(shù)都放入內(nèi)存,所以DOM的內(nèi)存使用量是依賴于輸入資料的大小,而SAX只基于XML樹(shù)的最大深度以及XML屬性存儲(chǔ)的最大資料。SAX處理文件的讀取解析會(huì)很快速,但是并不適用于快速查找和更新。
iOS中使用NSXMLParser事件驅(qū)動(dòng)解析器,即SAX方式解析XML數(shù)據(jù)。
JSON
JSON JavaScript Object Notation JavaScript對(duì)象表示法 是一種輕量級(jí)的數(shù)據(jù)交換語(yǔ)言,以文字為基礎(chǔ),易于閱讀。JSON是Javascript的一個(gè)子集,但JSON是獨(dú)立于語(yǔ)言的文本格式,其數(shù)據(jù)格式與語(yǔ)言無(wú)關(guān)。JSON常用于WEB應(yīng)用以及JavaScript、Java、Node.js應(yīng)用的開(kāi)發(fā)
示例:
{
"firstName": "John",
"lastName": "Smith",
"isAlive": true,
"age": 25,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "office",
"number": "646 555-4567"
},
],
"children": [],
"spouse": null
}
因?yàn)樽鳛镴avaScript的子集,一般JSON會(huì)使用eval()作為讀取數(shù)據(jù)的方式,但這種方式只能針對(duì)可靠的數(shù)據(jù)來(lái)源,因?yàn)?code>eval()同樣可以執(zhí)行任意JavaScript代碼。此外可以使用JSON.parse(str), parseJSON這種具有安全檢查的方法來(lái)讀取。
與XML相比,XML是一個(gè)完整的標(biāo)記語(yǔ)言,而JSON不是。XML利用標(biāo)記語(yǔ)言的特性提供了絕佳的延展性,在數(shù)據(jù)存儲(chǔ)、擴(kuò)展以及高級(jí)檢索方面具備對(duì)JSON的優(yōu)勢(shì),而JSON則比XML更加小巧,并可以依賴于瀏覽器的內(nèi)建快速解析支持,所以會(huì)更適合于網(wǎng)絡(luò)數(shù)據(jù)的傳輸。
Android提供原生類用于解析JSON,iOS中使用NSJSONSerialization解析JSON數(shù)據(jù)。
數(shù)據(jù)保護(hù)
Mobile BI iOS Application中文檔內(nèi)容的保存主要使用Binary Data,以文件的形式存儲(chǔ)在iOS Storage中,部分XML格式的文件類似。所以我們數(shù)據(jù)的保護(hù)其實(shí)討論的就是如何保護(hù)這些存儲(chǔ)在Storage中的數(shù)據(jù)信息。
Cache Protection
在應(yīng)用的使用過(guò)程中,為了支持功能的高效運(yùn)轉(zhuǎn)以及離線功能的實(shí)現(xiàn),我們需要將一部分?jǐn)?shù)據(jù)緩存在設(shè)備上,這也就意味著我們需要對(duì)這些緩存進(jìn)行保護(hù),避免隨便泄露出去。
一個(gè)明顯的策略就是我們需要根據(jù)用戶的意愿以及數(shù)據(jù)本身的有效性,提供清理緩存數(shù)據(jù)的功能。我們?cè)O(shè)計(jì)出一系列會(huì)觸發(fā)緩存清理的項(xiàng)目,包括:
Data Wipe on Client Certificate expired
Clear Caches on Logout
Clear on Close
Clear Cached Data
Change Config
Change User
Change Connectivity Settings
Security Policy Updated
User enters incorrect DPC more than X times
Reset Application
Password Expired
Locale Change
Confidential Project
這個(gè)功能是使用一個(gè)"Cover Screen"遮蓋住應(yīng)用的內(nèi)容,而用戶需要輸入驗(yàn)證才能移除這個(gè)"Cover Screen"。這個(gè)功能的特殊點(diǎn)在于,用戶驗(yàn)證采用的是與Mobile BI的Project一致的憑證(即用戶名和密碼等),這一點(diǎn)滿足了一部分客戶的需求。
但是缺點(diǎn)也是顯而易見(jiàn):只是蓋住了應(yīng)用的內(nèi)容,說(shuō)明應(yīng)用的內(nèi)容可以在用戶被驗(yàn)證前產(chǎn)生,這就有泄露的可能性;采用與Project一致的憑證,這就說(shuō)明要么需要同緩存的Project憑證相比較,要么需要通過(guò)網(wǎng)絡(luò)確認(rèn),都有將憑證暴露的危險(xiǎn)。因此這個(gè)功能現(xiàn)在已經(jīng)被移除。
DPC
DPC(Device Protection Code) "Designed to prevent unauthorized access to the Mobile application",將用戶輸入的密碼與應(yīng)用中加密密鑰綁定,確保只有在用戶輸入有效密碼后才能解密應(yīng)用數(shù)據(jù),利用這一特性,可以取代Confidential Project這個(gè)功能。
對(duì)于用戶輸入的密碼,我們不希望應(yīng)用直接記錄下來(lái),因?yàn)橹灰涗洠蜁?huì)存在不安全的可能性。而對(duì)于應(yīng)用自身使用的加密方式,一般會(huì)為了速度而選擇AES這種速度較快的加密方式。我們需要一種策略來(lái)建立兩者之間的聯(lián)系,并保證兩者的安全要求都能夠得到滿足:
Data Encryption Key
- Stored in the keychain with the encrypted format
Password Encryption Key
- Encrypt the Data Encryption Key
我們?cè)O(shè)計(jì)了這兩個(gè)加密密鑰,一個(gè)數(shù)據(jù)密鑰用來(lái)加解密應(yīng)用數(shù)據(jù),被以加密的形式存儲(chǔ)在Keychain中,另一個(gè)密碼密鑰用來(lái)解密這個(gè)數(shù)據(jù)密鑰,而這個(gè)密碼密鑰是直接通過(guò)用戶的輸入密碼而生成的。使用這種策略,可以保證:
用戶的密碼是可變的,這樣保證了在用戶修改密碼的情況下,也不需要更新數(shù)據(jù)密鑰,從而保證加密 的應(yīng)用數(shù)據(jù)可以繼續(xù)被使用。
密碼密鑰由用戶輸入直接生成,不需要存儲(chǔ),保證了安全性。
獨(dú)立的兩個(gè)密鑰滿足了不同的要求:應(yīng)用數(shù)據(jù)量大,對(duì)加解密的效率要求高;密碼密鑰只在應(yīng)用打開(kāi)時(shí)使用,對(duì)安全要求度高
我們使用PBKDF來(lái)生成密碼密鑰,這屬于CPU密集型算法,基本規(guī)則就是將輸入的Salted Hash進(jìn)行多次重復(fù)計(jì)算,而這個(gè)次數(shù)是可設(shè)定的,使用這種策略可以有效的對(duì)應(yīng)類似于彩虹表這種攻擊。對(duì)于首次生成PBKDF密鑰:
選用"256位的Salt",計(jì)算Iteration,確認(rèn)"SHA256 哈希算法",使用"Password + Salt + Iteration"組合生成 "256位" 的PBKDF Key
在使用的過(guò)程中,為了安全性,我們不會(huì)直接保存這個(gè)密碼密鑰值,而只是把生成這個(gè)值所需要的信息保存起來(lái),加上用戶輸入的密碼,每次驗(yàn)證時(shí)使用這些信息生成密鑰值。加上保存一個(gè)Message以及相應(yīng)HMAC值的方式,使用這個(gè)新生成的密鑰值加密Message與保存的HMAC值進(jìn)行比對(duì)來(lái)驗(yàn)證用戶:
信息保存至Keychain:Salt, Iteration 用來(lái)生成PDKDF;Message, HMAC 用于驗(yàn)證
數(shù)據(jù)加密
Mobile BI中包含大量的報(bào)表數(shù)據(jù)信息,這些數(shù)據(jù)會(huì)被保存在設(shè)備的本地存儲(chǔ)上面,相應(yīng)的我們也提供了不同級(jí)別的數(shù)據(jù)加密方式來(lái)保護(hù)這些數(shù)據(jù)。
文件加密
對(duì)于iOS設(shè)備來(lái)說(shuō),在使用iOS的文件API時(shí),我們可以相應(yīng)的選擇文件保護(hù)的方式,即稱為Hardware Encryption,保護(hù)的級(jí)別被設(shè)定為NSDataWritingFileProtectionComplete,這代表文件是被加密保存的,并且只有在設(shè)備在解鎖狀態(tài)下才能夠被訪問(wèn)。
在設(shè)備被鎖的情況下,我們相應(yīng)的提供了Software Encryption的方式,即我們自己在保存數(shù)據(jù)之前會(huì)對(duì)數(shù)據(jù)加密,這種方式也可以應(yīng)用到全局,即可實(shí)現(xiàn)軟硬同時(shí)加密的方式。
目前我們采用的是256位AES加密方式,我們會(huì)通過(guò)一系列的方式確保生成一個(gè)足夠安全的數(shù)據(jù)加密密鑰:
Data Encryption Key
數(shù)據(jù)加密密鑰,其實(shí)就是一段固定長(zhǎng)度的隨機(jī)數(shù)據(jù),當(dāng)然要確保足夠隨機(jī)和安全,示例如下:
uint8_t* newKeyBytes = malloc(keySize * sizeof(uint8_t));
memset((void *)newKeyBytes, 0x0, keySize);
OSStatus result = SecRandomCopyBytes(kSecRandomDefault, kCCKeySizeAES256, newKeyBytes);
NSData* newKey = [[NSData alloc] initWithBytes:(const void *)newKeyBytes length:kCCKeySizeAES256];
Salting
密碼加鹽Salting是密碼存儲(chǔ)時(shí)采用的一種安全方法,即加一些隨機(jī)的數(shù)據(jù)到已有密碼里,可以保證同樣的密碼能夠生成不同的散列,提高破解難度:
Appending some random data to the plaintext before hashing
- A best practice for password storage: ensures that the same password doesn’t always result in the same hash
- The Same principle for which we introduced the random IV for credentials encryption
Random IV
初始向量 Initialization Vector 是在加密過(guò)程中引入的一個(gè)隨機(jī)數(shù)據(jù),確保同樣的密碼可以生成不同的加密結(jié)果:
A Random IV: avoid against Same Text Attack
- A text will always be encrypted to same encrypted text
- The IV need not be secure hence we pre-append it to the encrypted text so that we can use it as input while decryption
加密流程
加密的數(shù)據(jù)由 頭數(shù)據(jù)Header、初始向量IV、數(shù)據(jù)Data 三者構(gòu)成,其中的頭數(shù)據(jù)包含了加密使用的算法,加密版本等信息。而加密時(shí)需要以下設(shè)定:算法 AES、工作模式 CBC、填充 PKCS7 Padding、密鑰長(zhǎng)度 256、IV在CBC中使用。
另外,我們?cè)谧罱K存儲(chǔ)時(shí),會(huì)使用Base64將結(jié)果編碼,等于是進(jìn)一步混淆。
憑證加密
區(qū)別于數(shù)據(jù)文件的可選 Sofware Encyprtion 加密方式,對(duì)于憑證信息,包括用戶名密碼這些敏感信息,我們是默認(rèn)加密后再保存的。密碼加密的方式流程與文件加密一致,采用256位AES加密算法。
不僅于此,我們還提供了多種設(shè)置可以配置密碼的存儲(chǔ)、失效、改動(dòng)等以便更好的提高安全性:
Never Cache Credentials
Never Persist Credentials
Password Expiration
Password Has Expired
Must Change Password
iOS Coding
PBKDF
PBKDF 即 Password Based Key Derivation Function
iOS中提供的PBKDF的方法如下:

它包含的一些參數(shù)的信息如下:
"algorithm" 僅支持 PBKDF2
"password" 即輸入的密碼文本
"salt" 用來(lái)混淆的salt鹽
"prf" Pseudo Random Algorithm 偽隨機(jī)算法
kCCPRFHmacAlgSHA1
kCCPRFHmacAlgSHA224
kCCPRFHmacAlgSHA256
kCCPRFHmacAlgSHA384
kCCPRFHmacAlgSHA512
"rounds" PRF的使用次數(shù)
"derivedKey" 即生成的密鑰
"derivedKey" 即生成的密鑰的長(zhǎng)度
在我們的應(yīng)用中選用kCCPRFHmacAlgSHA256算法以及設(shè)定生成的密鑰長(zhǎng)度為kCCKeySizeAES256
CCCalibratePBKDF
根據(jù)設(shè)定的要求計(jì)算出PRF需要循環(huán)的次數(shù):

它的參數(shù)信息如下:
"algorithm" 僅支持 PBKDF2
"passwordLen" 密碼文本的長(zhǎng)度
"saltLen" 混淆鹽的長(zhǎng)度
"prf" Pseudo Random Algorithm 偽隨機(jī)算法
"derivedKeyLen" 生成密鑰的預(yù)期長(zhǎng)度
"msec" 結(jié)合以上參數(shù)的情況下,所預(yù)期的計(jì)算時(shí)間
SecRandomCopyBytes
使用生成的安全隨機(jī)數(shù)作為混淆鹽 Salt

CCHmac
密鑰散列消息認(rèn)證碼生成函數(shù)

CCCrypt
用來(lái)實(shí)現(xiàn)加解密的函數(shù),基本是順序?qū)崿F(xiàn)了:
CCCrytorCreate(), CCCryptorUpdate(), CCCryptorFinal(), and CCCryptorRelease()

參數(shù)信息:
"op" 即設(shè)定加解密模式
kCCEncrypt
kCCDecrypt
"alg" 指定使用的算法
kCCAlgorithmAES
kCCAlgorithmDES
kCCAlgorithm3DES
kCCAlgorithmCAST
kCCAlgorithmRC4
kCCAlgorithmRC2
kCCAlgorithmBlowfish
"options" 確定分組密碼的工作模式,默認(rèn)為CBC模式
kCCOptionPKCS7Padding: Perform PKCS7 padding
kCCOptionECBMode: Electronic Code Book Mode
"iv" 引入的初始化向量,在CBC模式下需要與密碼塊大小一致
"key", "keyLength" 即使用的密鑰
引用
Wikipedia Extensible Markup Language