10+個Android 安全設計風險實務個案討論
設計一個安全的 android App相對來說是比較困難的。
怎麼說呢? 因為Mobile App安裝在手機端,
使用者或是駭客可以用各式各樣的方式將 App反向工程、進行各種非預期的操作與輸入行維。
因此,設計一個安全的手機成是相對有一定的難度。
這篇文章主要舉幾個筆者在進行Android評審時常遇到的個案,
藉由這些個案實例說明設計一個安全Android App要注意的事項。
1. AndroidManifest.xml 設定三個原則
- 1. Do not specify taskAffinity
- 2. Do not specify launchMode
- 3. Explicitly set the exported attribute to false.
為什麼呢? 簡單來說,Android 個個程式間的溝通,如果沒有適當的安全設定,
敏感性的訊息有可能會經由這些溝通管道被其他惡意程式取得。
另外,上線前記得將 debuggable 移除。
//危險的範例設定
<application
. . .
android:debuggable="true"
. . . >
. . .
</application>
|
allowBackup 的設定會導致用戶可以使用 adb backup 對於整個資料庫讀取(無須 root),間接造成資料外洩。
//危險的範例設定
<application
. . .
android:allowBackup="true"
. . . >
. . .
</application>
|
2. Set FLAG_ACTIVITY_NEW_TASK
盡量不要在 startActivity 改變啟動模式。
// 有害的程式
Intent intent = new Intent(this, RISKY_ACTIVITY.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
|
Broadcasts
廣播的訊息避免其他應用程式也能收到。另外,無須額外設定 “intent-filter”
建議的設定:
<receiver
. . .
android:exported=“false”
. . . >
. . .
</receiver>
|
3. Content Providers
雖然可以透過 FLAG_GRANT_READ_URI_PERMISSION 設定
讓其他應用程式也可以有暫時對於 intent的讀取權限,但是盡量避免。
Intent intent = new Intent();
. . .
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setData(REQUEST_URI);
. . .
|
4. Services
Services 應該設定為避免其他應用程式讀取,減少非預期的結果。
//建議的設定
<service
. . .
android:exported="false">
. . .
</service>
|
5. 檔案儲存在公共目錄或是 SD Card
對於檔案在公共目錄的儲存或是外部記憶卡盡量避免。
如果真的要儲存,不要儲存敏感性資訊,例如帳號、密碼、信用卡等。
[pastacode lang=”markup” message=”” highlight=”” provider=”manual”]
// 危險程式
openFileOutput("/sdcard/data/vulnerability.txt", Context.MODE_PRIVATE);
[/pastacode]
6. 建立檔案時的權限設定
檔案的建立應該設定為 “MODE_PRIVATE”
// 危險的程式碼,因為設定為 "MODE_WORLD_WRITABLE"造成其他App也能讀取這個檔案。
// 因為危險性的關係,API level 17之後就不再支援
openFileOutput("vulnerability.txt", Context.MODE_WORLD_WRITABLE);
|
Log機制
如果該 app可以取得 Read_log的權限,那麼就可以任意讀取其他 App 的log。因此要確保 App 的 log沒有輸出敏感性資訊。
Android 4.0(API15) 之前的版本是有這樣的風險。Android 4.1(API16) 後,就算app取得這樣的 Read_logs權限也無法讀取其他app 的 log.
AndroidManifest.xml<uses-permission android:name=”android.permission.READ_LOGS”/> |
避免在 release 版本的 app中加入額外的訊息輸出。
//危險程式 // 避免敏感性資料的輸出 // 建議使用 Android 提供的android.util.Log,避免使用System.out/err if (...) { System.out.print(". . ."); //In release code } |
7. 去除”Log.d()/v()”
開發過程中所輸出的 log 都要去除。
//危險的程式碼
if (...) {
// Remove the following code from release build
Log.d("<Vulnerability>", "...");
}
|
一般來說 Log 提供 error, Warning, Info。開發過程我們使用 debug 或是 Verbose,上線後必須要移除。
// 避免將敏感性資料輸出至 LogLog.e(LOG_TAG, “Not sensitive information (ERROR)”); Log.w(LOG_TAG, “Not sensitive information (WARN)”); Log.i(LOG_TAG, “Not sensitive information (INFO)”); // 避免將敏感性資料輸出至 Log // 上線時這些 log需要移除 Log.d(LOG_TAG, “sensitive information (DEBUG)”); Log.v(LOG_TAG, “sensitive information (VERBOSE)”); |
註:ADT 21 之後的版本可以設定 BuildConfig.DEBUG
另外可以考慮使用 “ProGuard”,這個工具會將程式碼變得更難閱讀。
即使駭客做反向工程,看到的程式碼是不容易閱讀的。
8. WebView JavaScript 執行
原則上盡量避免 JavaScript 的執行,除非該網頁JavaScript的來源是可信任。
// 危險的程式設定setJavaScriptEnabled(true)
WebView webViewObj;
. . .
// It is risky when accessing untrusted servers or files stored in public folders.
webViewObj.getSettings().setJavaScriptEnabled(true);
|
避免任意載入JavaScript
[pastacode lang=”markup” message=”” highlight=”” provider=”manual”]
// 危險的程式範例
class LocalObject {
. . .
}
. . .
WebView webViewObj;
. . .
// insert an injectedObject object of LocalObject class into webViewObj
webViewObj.addJavascriptInterface(new LocalObject (),"injectedObject");
[/pastacode]
9. 使用舊版危險的函數 searchBoxJavaBridge_
Android 4.2 版本之前,這個物件有可能會被誤用來遠端執行 JavaScript
// 危險的程式範例
WebView webViewObj;
. . .
webViewObj .removeJavascriptInterface(“searchBoxJavaBridge_”);
. . .
|
10.快取的清除
對於瀏覽過的敏感性資訊網頁,建議可以清除快取。
//快取清除範例
WebView webViewObj;
. . .
webViewObj. clearCache(true);
. . .
|
11. SQL Injection
輸入的驗證與查詢參數的處理是很重要的。
下列程式由於對參數沒有特別的處理與驗證,因此會導致 SQL Injection
//危險的程式範例
Void query(String strParam) {
final String s1 = "select * from ";
String command = s1 + strParam;
if (command != null) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.execSQL(command);
}
}
|
12. 密碼Remember 設定
當App 有提供密碼Remember 時,建議還是需要與遠端網站伺服器進行驗證。
否則,當手機遺失,或是密碼資訊儲存在本機端時,該驗證資訊就很容易被駭客誤用。
利用 Flag_secure 的設定降低畫面擷取的攻擊方式
// 建議的程式碼
Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
13. 密碼欄位的輸入設定
<EditText
. . .
android:inputType="textPassword"
android:password="true"
. . . >
. . .
</EditText>
|
14. HTTPS 傳輸
使用 HTTPS避免使用 HTTP
另外,資訊參數的傳遞盡量避免使用 URL
http:/victim.com/loginname=abc×tamp=10002
15. 驗證 SSL Server正確
不要使用 “ALLOW_ALL_HOSTNAME_VERIFIER” ,
因為這會跳過 SSL Server 的驗證。
// 危險程式碼範例
SSLSocketFactory sf;
...
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
|
16. 驗證 App Issuer
由於 Android App 可容易受到修改,因此必須適當的驗證App的發佈來源
17. 避免 Click Fraud
惡意程式可能會利用 Toast的特性誘導使用者點擊,
或是讓使用者誤以為這是原本應用程式的一部分。
因此可以透過這個設定,讓應用程式執行時,不會出現其他應用程式的 Toast
<TextView
. . .
android:filterTouchesWhenObscured="true"
. . . />
|
18. 是否可以備份還原資料
預設值為 True.
如果沒有設定為 false,駭客可以利用 adb 將應用程式的資料拷貝。
<application
. . .
android:allowBackup="false"
. . . >
. . .
</application>
|
19. Intent 的輸入值檢查
Android 透過 Intent相互傳遞資訊,
程式使用getExtra(), getBundleExtra(), getCharExtra(), etc.
必須檢查回傳值、是否為 NULL、是否合法
// 安全的程式碼範例
Bundle bundle = intent.getBundleExtra("key");
if (bundle == null) {
//return or other error handling
}
|
20. 關於定位隱私的問題
如果要在 WebView使用定位,通常需要下列權限。
由於”位置”資訊也是個人隱私的一部分,所以當需要下列權限時,必須取得使用者同意。
- android.permission.ACCESS_FINE_LOCATION
- android.permission.ACCESS_COARSE_LOCATION
- android.permission.INTERNET
- WebSettings#setGeolocationEnabled(true);
由於許多的 app 並沒有提醒使用者要需要取得定位資訊,因此駭客可以製造一個網站,
誘導使用者瀏覽該網站就可以取得使用者的位置資訊。
21 壓縮檔案炸彈
駭客可以透過壓縮檔案炸彈讓手機應用程式癱瘓。因此基本要做到的防護措施為
1. 確定該來源 ZIP是預期要解壓縮的檔案
2. 解壓縮前計算檔案大小,範例程式如下:
[pastacode lang=”java” message=”” highlight=”” provider=”manual”]
public final void unzip(String filename) throws java.io.IOException{
FileInputStream fis = new FileInputStream(filename);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
ZipEntry entry; int entries = 0; int total = 0;
try {
while ((entry = zis.getNextEntry()) != null) {
System.out.println("Extracting: " + entry);
int count;
byte data[] = new byte[BUFFER];
// output a file AFTER verifying filenams and resulting file size
String name = validateFilename(entry.getName(), ".");
FileOutputStream fos = new FileOutputStream(name);
BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);
while (total <= TOOBIG && (count = zis.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, count);
total += count;
}
dest.flush();
dest.close();
zis.closeEntry();
entries++;
if (entries > TOOMANY) {
throw new IllegalStateException("Too many files to unzip.");
}
if (total > TOOBIG) {
throw new IllegalStateException("File being unzipped is too big.");
}
}
} finally { zis.close(); } }
[/pastacode]
30+. 權限設定
權限有很多。過多與過少都不好。需要用到在設定即可。以下是比較需要注意的權限。
- android.permission.INTERNET
- Allows an application to open network sockets.
- android.permission.READ_LOGS
- Allows an application to read the low-level system log files. Log entries can contain the user’s private information.
- android.permission.ACCESS_COARSE_LOCATION
- Allows an application to access coarse (such as, Cell-ID, Wi-Fi) locations.
- android.permission.CAMERA
- Allows an application to access the camera device.
- android.permission.ACCESS_FINE_LOCATION
- Allows an application to access fine (such as, GPS) locations.
- android.permission.CHANGE_WIFI_STATE
- Allows an application to change the Wi-Fi connectivity state.
- android.permission.WRITE_SETTINGS
- Allows an application to read or write system settings.
- android.permission.MOUNT_UNMOUNT_FILESYSTEMS
- Allows an application to mount and unmount file systems for removable storage.
- android.permission.READ_SMS
- Allows an application to read SMS messages.
- android.permission.PROCESS_OUTGOING_CALLS
- Allows an application to monitor, modify, or abort outgoing calls.
- android.permission.RECEIVE_SMS
- Allows an application to monitor incoming SMS messages, to record or perform processing on them.
- android.permission.ACCESS_FINE_LOCATION
- Allows an application to access fine (such as, GPS) locations.
- android.permission.RECEIVE_BOOT_COMPLETED
- Allows an application to receive ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.
- android.permission.ACCESS_NETWORK_STATE
- Allows an application to access information about networks.
- android.permission.READ_PHONE_STATE
- Allows read-only access to the phone state.
- android.permission.WRITE_EXTERNAL_STORAGE
- Allows an application to write to external storage.
- android.permission.ACCESS_WIFI_STATE
- Allows an application to access information about Wi-Fi networks.
- android.permission.GET_ACCOUNTS
- Allows an application to access the list of accounts in the Accounts Service.
- android.permission.KILL_BACKGROUND_PROCESSES
- Allows an application to kill processes running in the background.
- android.permission.MODIFY_AUDIO_SETTINGS
- Allows an application to modify global audio settings.
- android.permission.BROADCAST_STICKY
- Allows an application to broadcast sticky intents. These are broadcasts about whose data is held by the system after being finished, so that clients can quickly retrieve that data without having to wait for the next broadcast.
- android.permission.VIBRATE
- Allows an application to access the vibrator.
- android.permission.RECEIVE_BOOT_COMPLETED
- Allows an application to receive ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.
- android.permission.BATTERY_STATS
- Allows an application to collect battery statistics.
- android.permission.READ_PHONE_STATE
- Allows read-only access to the phone state.
- android.permission.WRITE_EXTERNAL_STORAGE
- Allows an application to write to external storage.
- android.permission.GET_ACCOUNTS
- Allows an application to access the list of accounts in the Accounts Service.
- android.permission.WAKE_LOCK
- Allows an application to use PowerManager WakeLocks to keep the processor from sleeping or the screen from dimming.
- android.permission.INTERNET
- Allows an application to open network sockets.
- android.permission.SYSTEM_ALERT_WINDOW
- Allows an application to open windows on top of all other applications.
android.permission.ACCESS_WIFI_STATE
- Allows an application to access information about Wi-Fi networks.
- android.permission.WAKE_LOCK
- Allows an application to use PowerManager WakeLocks to keep the processor from sleeping or the screen from dimming.
- android.permission.ACCESS_NETWORK_STATE
- Allows an application to access information about networks.
- android.permission.READ_EXTERNAL_STORAGE
- Allows an application to read from external storage.
- android.permission.READ_PHONE_STATE
- Allows read-only access to the phone state.
- android.permission.WRITE_EXTERNAL_STORAGE
- Allows an application to write to external storage.