2013年10月23日 星期三

淺談 Security

Android 的安全性依賴於本身系統的設計,  主要的設計如下

* Sandbox 的設計, 讓App的資料與程式碼不為其他 App 所用
* Android 本身的架構設計就包含了 cryptography(加密), permissions, IPC (inter-process communication 進程間通訊).
* 還有些降低 memory 出錯的設計, 如: ASLR, NX, ProPolice....等
* 加密的檔案系統, 即使裝置遺失或被偷, 依然可以保護 data 免於被取出
* 使用者的 permission 設計, 讓使用者可以自行限制系統資源與使用者資料的讀取.
* 利用 App 設定的 permission 來控制 application 在每個 app 的基本資料.

藉由熟悉 Android 的 security 設計, 可以讓我們更容易地降低使用者的資安問題.

資料儲存 Storing Data

Android 的資料儲存分為三種:

使用內部儲存 Using internal storage

在預設的情況下, internal storage 的資料只能夠被自身的 App 讀取, 在大部分的情況下已然夠用.

要盡量避免使用 MODE_WORLD_WRITEABLE 或 MODE_WORLD_READABLE 來做 IPC 檔案, 因為這麼做的話, 所有的 App 都能讀到自身 App 的任何(格式)資料. 正確的做法是利用 content provider 來提供資料給某些你認同的 App.

要更謹慎處理的資料, 可以透過 KeyStore 來替該資料加密. 這個 Key 的密碼不直接存在裝置上, 如此便降低了遺失時的風險. 需要提防的是, 如果竊取者有 root 權限, 在輸入密碼的時候還是會被擷取到. (此舉應該是在認證還未過時, 更降低被破解的可能)

使用外部儲存 Using external storage

在 external storage (ex. SD卡) 上的資料, 通常可以被其他程式寫入並讀取. 因為外部儲存裝置可以被使用者取下, 所以最好不要存入任何重要資料.

因為 external storage 的資料可以被寫入, 我們使用前應該做 input validation 的測試. 最好不要將可執行的檔案放在 external storage, 而是應該透過伺服器作動態讀取. 如果真的要使用, 也應該加密並驗證.

使用 content providers

Content provider 是 Android 用來與其他 App 交流資料的機制. 如果設定permission 為 android:exported=false 則資料不會被其他 App 讀取到. 反之, 設 true 才能提供資料給其他 App.

透過  permission 可以控制其他 App 讀取或寫入的權限, 應該在一開始就設好, 畢竟之後修改的話可能就會影響到已使用的用戶.

如果 content provider 只有在自己開發的 Apps 用到, 可以設 android:protectionLevel="signature". 這樣 user 就不必認證, 可以有較佳的使用者體驗.

更細節一些, 可以提供 android:grantUriPermissions 的 permission, 這樣就可以進一步限制 permission 的路徑.

一但使用了 content provider, 最好也使用 query(), update(), delete() 這些帶有參數的方法, 可以避免一些不必要的 SQL 輸入錯誤.

要注意的是只提供 write 的 permission 並不表示竊資者無法讀取資料, 因為他們依然可以修改 WHERE 的參數來取得資料. 因此, 有了 write 的權限, 對竊取者來說也有了 read 的權限.

用戶的  Permissions

因為 Android 不同的 Apps 之間權限是相隔開的, 必須透過 permission 才能取得彼此的資源. 包括相機, 網路...等等.

要求 Permissions

原則上如果能不需要這個 permission, 那就不要去要求 permission.
Permission 要求的越少, 越不容易有資料外洩的問題.

創造 Permission

也可以要求 Android 系統預設之外的 permission,
有分 "signature" protection level 跟 "dangerous" protection level.
"dangerous" protection level 不為使用者接受的可能性較高些.

網路的使用 Using Networking

網路傳輸相對上比較危險一些, 可能會泄漏使用者的個資, 因此要小心處理.

網路 IP 的使用

盡量使用 HTTPS 的伺服器而非 HTTP, 因為行動裝置連上的網路通常沒有安全防護, 例如 wifi 熱點.

如果是使用 Socket 通訊, 可以使用 SSLSocket 這個 class. 建議整個通訊的內容都要特別加密.

在 IPC 的使用上, 必須使用 Android IPC mechanism; 不要使用 localhost, 因為這個界面可能會被其他任何的 App 所觀察到. (INADDR_ANY 也不能用, 因為自身的 App 會收到來自任何地方的 request)

此外, 也不要相信其它 Http 或不安全協定下載的檔案.

電話網路的使用 Using Telephony Networking

SMS (簡訊) 是設計給用戶間通訊用的, 不適合檔案傳輸. 建議使用 Google Cloud Messaging (GCM) 以及 IP 網路來傳遞檔案.

SMS 沒有經過特別的加密, 任何 SMS 的接收器都可能會收到惡意的 SMS. 因此不要信任這些 SMS. 當手機收到 SMS 時, 會發送 braoadcast intents, 因此任何有 READ_SMS permission 的 App 都可以接收到.

使用輸入驗證 Performing Input Validation

不正確的輸入驗證, 可能會導致 App 無法順利執行.  建議使用Android 系統提供的驗證.

如果是開發者使用自己的程式碼, 可能產生如 buffer overflow, use after free, 和 off-by-one errors 等問題. Android 有提供 ASLR(Address Space Layout Randomization) 以及 DEP(Data Execution Prevention) 來協助處理. (但最根本的 pointers 跟 buffer 還是要自己處理.)

動態上, JavaScript 跟 SQL 都有可能被 script injection (腳本攻擊, 因為 JavaScript 跟 SQL 都是 Script Language)

要減小 SQL injection 的方式是使用 content providers 提供的接口, 以及設置 permission.

雖然 blacklisting 也是一種方法, 但是不建議. ( blacklisting 只能預想切取者如何攻擊)

使用者的個資處理 Handling User Data

原則上, 越少索取 User data 就會越安全一些. 如果你取得了 User 資料可以不儲存, 也可以不傳遞, 那就不要. 如果把 email 當作是 hash 的 (primary) key, 就能減少所有資料被竊的可能.

如果開發者的應用儲存的使用者的名稱與密碼, 記得要對個資的使用作聲明. 避免有法律上的問題.

另外, 要減少暴露個資給第三方 App 的機會, 最好的方式就是減少個資讀取.

如果個資能不上傳到網站, 則盡量別上傳,  在本機端處理就好.

謹慎處理 IPC 的資料, 確認個資不會外洩.

如果需要長串的辨識碼(GUID), 不要使用手機號碼 或 IMEI .

Log 的寫入也要小心, 因為其他 App 也看得到 Log 的資料.

使用 WebView

WebView 的資料是由 HTML 與 JavaScript 所構成, 不當使用就會有 cross-site-scripting (JavaScript injection) 的問題.

如果, 你的應用沒有使用到 JavaScript, 則不要設  setJavaScriptEnabled(). (預設是沒有開啟)

使用 addJavaScriptInterface() 也是一樣必須小心, 只連上值得信任的網站是比較安全的. 最好是只有當 App 的 APK 本身有使用到 JavaScript 才開啓這項.

另外, 使用 clearCache() 可以刪除本機端的 Cache; 伺服器端如果 header 有 no-cache 則可避免資料存在手機端.

憑證處理 Handling Credentials

要避免資料外洩, 就要減少憑證要求, 也不要將個資存於手機, 尤其是賬號密碼.

比較好的方式是,  使用者透過賬號密碼取得 token; token 的生存時間短, 減少被竊的危險.

如果開發者的服務 (Service) 可以被很多 Apps 使用, 則使用 AccountManager 這個 class, 並且不用將密碼存於裝置上是比較好的. 此外, 也要注意 AccountManager 取回的 Account 別傳給錯誤的 App.

如果某個憑證(Credential) 只有開發者的 Apps 能用, 則可使用  checkSignature() 來達成 App 的認證. 相對的, 如果只有一個 App 會使用, 則可用 KeyStore 來儲存.

加密使用 Using Cryptography

網路的使用最好使用 HttpsURLConnection 或  SSLSoket.

如果要自行加密, 則有 Cipher 這個  class. 也有 SecureRandom, KeyGenerator 來產生加密代碼.

如果某個 Key 要重復用, 則可用 KeyStore 來加密儲存.

使用 IPC  Using Interprocess Communication

Android 的 IPC 方式有 Intent, Binder 或 Messenger, 加上Service, BroadCastReceiver.
如果不需要被其他 App 使用, 則 android:exported 設為 "false".
如果只給開發者的 Apps 使用, 則 android:protectionLevel 設為 "signature".

Intent 使用,  Using intent

Intent 有分 explicit 跟 implicit,
explicit intent 會直接呼叫開發者指定的 class,
implicit intent 會透過 intent filter 以及 permission 查看有哪些 class 可接收這個 intent.

所以傳遞重要資料時, 更要特別小心 permission 的設置.

Service 使用 Using Services

Service 預設不能為其他 App 所用, 但如果透過 intent filter 就可以截取 implicit intent.

使用 Binder 跟 Messenger Interfaces, Using binder and messenger interfaces

Binder 跟 Messenger 並不需要另設 permission, 它們的 permission 會延伸至 Service 或 Activity.

使用 Broadcast Receiver, Using broadcast receivers

BroadcastReceiver 預設可以接受任何的 intent, 但可以透過 permission 來限制只有特定的 intent 可以使用.

動態讀取程式碼, Dynamically Loading Code

不建議, 因為非常容易就會發生 code injection 的問題.

模擬器的安全性, Security in a Virtual Machine

模擬器有兩點需要注意:
* 模擬器的 Apps 之間沒有 sandboxes 隔開
* 使用時注意資料下載的來源, 避免惡意網路下載竊取資料.

Native Code 的安全性, Security in Native Code

不建議使用 Linux 原生語言, 可能會產生其他的問題. 如果真使用時, 也要瞭解 Linux 本身的安全性.

Android 的 Apps 之間有 sandboxes, 即使使用原生語言也是一樣. 可以想成是每個 App 自己有不同的 UID.



參考來源: http://developer.android.com/training/articles/security-tips.html






沒有留言:

張貼留言