阿里Android開發規範:安全與其他

leeyh發表於2018-03-06

以下內容摘自 阿里巴巴Android開發手冊

我們的目標是:

  • 防患未然,提升質量意識,降低故障率和維護成本;
  • 標準統一,提升協作效率;
  • 追求卓越的工匠精神,打磨精品程式碼。
  • 【強制】必須遵守,違反本約定或將會引起嚴重的後果;
  • 【推薦】儘量遵守,長期遵守有助於系統穩定性和合作效率的提升;
  • 【參考】充分理解,技術意識的引導,是個人學習、團隊溝通、專案合作的方向。

阿里Android開發規範:資原始檔命名與使用規範
阿里Android開發規範:四大基本元件
阿里Android開發規範:UI 與佈局
阿里Android開發規範:程式、執行緒與訊息通訊
阿里Android開發規範:檔案與資料庫
阿里Android開發規範:Bitmap、Drawable 與動畫
阿里Android開發規範:安全與其他

1、【強制】使用 PendingIntent 時,禁止使用空 intent,同時禁止使用隱式 Intent 說明:

  1. 使用 PendingIntent 時,使用了空 Intent,會導致惡意使用者劫持修改 Intent 的內容。禁止使用一個空 Intent 去構造 PendingIntent,構造 PendingIntent 的 Intent一定要設定 ComponentName 或者 action。
  2. PendingIntent 可以讓其他 APP 中的程式碼像是執行自己 APP 中。PendingIntent的intent接收方在使用該intent時與傳送方有相同的許可權。在使用PendingIntent時,PendingIntent 中包裝的 intent 如果是隱式的 Intent,容易遭到劫持,導致資訊洩露。

正例:

Intent intent = new Intent(this, SomeActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
try {
	pendingIntent.send();
} catch (PendingIntent.CanceledException e) {
	e.printStackTrace();
}
複製程式碼

反例 1:

Bundle addAccountOptions = new Bundle();
mPendingIntent = PendingTntent.getBroadcast(this, 0, new Intent, 0);
addAccountOptions.putParcelable(KEY_CALLER_IDENTITY, mPendingIntent);
addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS,
Utils.hasMultipleUsers(this));
AccountManager.get(this).addAccount(accountType,
	null,
	null,
	addAccountOptions,
	null,
	mCallback,
	null);
複製程式碼

反例 2: mPendingIntent 是通過 new Intent()構造原始 Intent 的,所以為“雙無”Intent,這個PendingIntent最終被通過AccountManager.addAccount 方法傳遞給了惡意APP介面。

Intent intent = new Intent("com.test.test.pushservice.action.METHOD");
intent.addFlags(32);
intent.putExtra("app",msg);
PendingIntent.getBroadcast(this, 0, intent, 0));
複製程式碼

如上程式碼PendingIntent.getBroadcast,PendingItent中包含的Intent為隱式intent, 因此當 PendingIntent 觸發執行時,傳送的 intent 很可能被嗅探或者劫持,導致 intent 內容洩漏。 擴充套件參考:

  1. developer.android.com/reference/a…
  2. wiki.sei.cmu.edu/confluence/…
  3. www.droidsec.cn/android-bro…

2、【強制】禁止使用常量初始化向量引數構建 IvParameterSpec,建議 IV 通過隨機方式產生。 說明: 使用固定初始化向量,結果密碼文字可預測性會高得多,容易受到字典式攻擊。iv的作用主要是用於產生密文的第一個 block,以使最終生成的密文產生差異(明文相同的情況下),使密碼攻擊變得更為困難,除此之外 iv 並無其它用途。因此 iv 通過隨機方式產生是一種十分簡便、有效的途徑。
正例:

byte[] rand = new byte[16];
SecureRandom r = new SecureRandom();
r.nextBytes(rand);
IvParameterSpec iv = new IvParameterSpec(rand);
複製程式碼

反例:

IvParameterSpec iv_ = new IvParameterSpec("1234567890".getBytes());
System.out.println(iv_.getIV());
複製程式碼

3、 【強制】將 android:allowbackup 屬性設定為 false,防止 adb backup 匯出資料。 說明: 在 AndroidManifest.xml 檔案中為了方便對程式資料的備份和恢復在 Android APIlevel 8 以後增加了 android:allowBackup 屬性值。預設情況下這個屬性值為 true,故當 allowBackup 標誌值為 true 時,即可通過 adb backup 和 adb restore 來備份和恢復應用程式資料。
正例:

<application
	android:allowBackup="false"
	android:largeHeap="true"
	android:icon="@drawable/test_launcher"
	android:label="@string/app_name"
	android:theme="@style/AppTheme" >
複製程式碼

4、【強制】在實現的 HostnameVerifier 子類中,需要使用 verify 函式效驗伺服器主機名的合法性,否則會導致惡意程式利用中間人攻擊繞過主機名效驗。 說明: 在握手期間,如果 URL 的主機名和伺服器的標識主機名不匹配,則驗證機制可以回撥此介面的實現程式來確定是否應該允許此連線。如果回撥內實現不恰當,預設接受所有域名,則有安全風險。
反例:

HostnameVerifier hnv = new HostnameVerifier() {
	@Override
	public boolean verify(String hostname, SSLSession session) {
		// 總是返回 true,接受任意域名伺服器
		return true;
	}
};
HttpsURLConnection.setDefaultHostnameVerifier(hnv);
複製程式碼

正例:

HostnameVerifier hnv = new HostnameVerifier() {
	@Override
	public boolean verify(String hostname, SSLSession session) {
		//示例
		if("yourhostname".equals(hostname)){
			return true;
		} else {
			HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
			return hv.verify(hostname, session);
		}
	}
};
複製程式碼

5、【強制】利用 X509TrustManager 子類中的 checkServerTrusted 函式效驗伺服器端證照的合法性。
說明:
在實現的 X509TrustManager 子類中未對服務端的證照做檢驗,這樣會導致不被信任的證照繞過證照效驗機制。
反例:

TrustManager tm = new X509TrustManager() {
	public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		//do nothing,接受任意客戶端證照
	}
	public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		//do nothing,接受任意服務端證照
	}
	public X509Certificate[] getAcceptedIssuers() {
		return null;
	}
};
sslContext.init(null, new TrustManager[] { tm }, null);
複製程式碼

6、【強制】META-INF 目錄中不能包含如.apk,.odex,.so 等敏感檔案,該資料夾沒有經過簽名,容易被惡意替換。
7、【強制】Receiver/Provider 不能在毫無許可權控制的情況下,將 android:export 設定為 true。
8、【參考】資料儲存在 Sqlite 或者輕量級儲存需要對資料進行加密,取出來的時候進行解密。
9、 【強制】阻止 webview 通過 file:schema 方式訪問本地敏感資料。
10、【強制】不要廣播敏感資訊,只能在本應用使用 LocalBroadcast,避免被別的應用 收到,或者 setPackage 做限制。
11、【強制】不要把敏感資訊列印到 log 中。
說明:
在 APP 的開發過程中,為了方便除錯,通常會使用 log 函式輸出一些關鍵流程的資訊,這些資訊中通常會包含敏感內容,如執行流程、明文的使用者名稱密碼等,這會讓攻擊者更加容易的瞭解 APP 內部結構方便破解和攻擊,甚至直接獲取到有價值的敏感資訊。
反例:

String username = "log_leak";
String password = "log_leak_pwd";
Log.d("MY_APP", "usesname" + username);
Log.d("MY_APP", "password" + password, new Throwable());
Log.v("MY_APP", "send message to server ");
複製程式碼

以上程式碼使用 Log.d Log.v 列印程式的執行過程的 username 等除錯資訊,日誌沒有關閉,攻擊者可以直接從 Logcat 中讀取這些敏感資訊。所以在產品的線上版本中關閉除錯介面,不要輸出敏感資訊。
12、【強制】對於內部使用的元件,顯示設定元件的"android:exported"屬性為 false。 說明: Android 應用使用 Intent 機制在元件之間傳遞資料,如果應用在使用 getIntent(),getAction(),Intent.getXXXExtra()獲取到空資料、異常或者畸形資料時沒有進行異常捕獲,應用就會發生 Crash,應用不可使用(本地拒絕服務)。惡意應用可通過向受害者應用傳送此類空資料、異常或者畸形資料從而使應用產生本地拒絕服務。
13、【強制】應用釋出前確保 android:debuggable 屬性設定為 false。
14、【強制】使用 Intent Scheme URL 需要做過濾。 說明: 如果瀏覽器支援 Intent Scheme Uri 語法,如果過濾不當,那麼惡意使用者可能通過瀏覽器 js 程式碼進行一些惡意行為,比如盜取 cookie 等。如果使用了 Intent.parseUri函式 , 獲取的 intent 必須嚴格 過濾 , intent 至少包含addCategory(“android.intent.category.BROWSABLE”) , setComponent(null) ,setSelector(null)3 個策略。
正例:

// 將 intent scheme URL 轉換為 intent 物件
Intent intent = Intent.parseUri(uri);
// 禁止沒有 BROWSABLE category 的情況下啟動 activity
intent.addCategory("android.intent.category.BROWSABLE");
intent.setComponent(null);
intent.setSelector(null);
// 使用 intent 啟動 activity
context.startActivityIfNeeded(intent, -1)
複製程式碼

反例:

Intent intent = Intent.parseUri(uri.toString().trim().substring(15), 0);
intent.addCategory("android.intent.category.BROWSABLE");
context.startActivity(intent);
複製程式碼

擴充套件參考:

  1. jaq.alibaba.com/community/a…
  2. www.mbsd.jp/Whitepaper/…

15、【強制】金鑰加密儲存或者經過變形處理後用於加解密運算,切勿硬編碼到程式碼中。
說明:
應用程式在加解密時,使用硬編碼在程式中的金鑰,攻擊者通過反編譯拿到金鑰可以輕易解密 APP 通訊資料。
16、【強制】將所需要動態載入的檔案放置在 apk 內部,或應用私有目錄中,如果應用必須要把所載入的檔案放置在可被其他應用讀寫的目錄中(比如 sdcard),建議對不可信的載入源進行完整性校驗和白名單處理,以保證不被惡意程式碼注入。
17、【強制】除非 min API level >=17,請注意 addJavascriptInterface 的使用。 說明: API level>=17,允許 js 被呼叫的函式必須以@JavascriptInterface 進行註解,因此不受影響; 對於 API level < 17,儘量不要使用 addJavascriptInterface,如果一定要用,那麼:

  1. 使用 https 協議載入 URL,使用證照校驗,防止訪問的頁面被篡改掛馬;
  2. 對載入 URL 做白名單過濾、完整性校驗等防止訪問的頁面被篡改;
  3. 如果載入本地 html,應該會 HTML 內建在 APK 中,以及對 HTML 頁面進行完整性校驗。

18、【強制】使用 Android 的 AES/DES/DESede 加密演算法時,不要使用預設的加密模式ECB,應顯示指定使用 CBC 或 CFB 加密模式。 說明: 加密模式 ECB、CBC、CFB、OFB 等,其中 ECB 的安全性較弱,會使相同的銘文在不同的時候產生相同的密文,容易遇到字典攻擊,建議使用 CBC 或 CFB 模式。

  1. ECB:Electronic codebook,電子密碼本模式
  2. CBC:Cipher-block chaining,密碼分組連結模式
  3. CFB:Cipher feedback,密文反饋模式
  4. OFB:Output feedback,輸出反饋模式

19、【強制】不要使用 loopback 來通訊敏感資訊。
20、【推薦】對於不需要使用 File 協議的應用,禁用 File 協議,顯式設定 webView.getSettings().setAllowFileAccess(false),對於需要使用 File 協議的應用,禁止 File協議呼叫 JavaScript,顯式設定 webView.getSettings().setJavaScriptEnabled(false)。
21、【強制】Android APP 在 HTTPS 通訊中,驗證策略需要改成嚴格模式。 說明:Android APP 在 HTTPS 通訊中,使用 ALLOW_ALL_HOSTNAME_VERIFIER,表示允許和所有的 HOST 建立 SSL 通訊,這會存在中間人攻擊的風險,最終導致敏感資訊可能會被劫持,以及其他形式的攻擊。
反例:

SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
複製程式碼

ALLOW_ALL_HOSTNAME_VERIFIER 關閉 host 驗證,允許和所有的 host 建立SSL 通訊,BROWSER_COMPATIBLE_HOSTNAME_VERIFIER 和瀏覽器相容的驗證策略,即萬用字元能夠匹配所有子域名 ,STRICT_HOSTNAME_VERIFIER 嚴格匹配模式,hostname 必須匹配第一個 CN 或者任何一個 subject-alts,以上例子使用了 ALLOW_ALL_HOSTNAME_VERIFIER,需要改成 STRICT_HOSTNAME_VERIFIER。
22.【推薦】Android5.0 以後安全性要求較高的應用應該使用 window.setFlag(LayoutParam.FLAG_SECURE) 禁止錄屏。
23.【推薦】zip 中不建議允許../../file 這樣的路徑,可能被篡改目錄結構,造成攻擊。 說明:當 zip 壓縮包中允許存在"../"的字串,攻擊者可以利用多個"../"在解壓時改變 zip 檔案存放的位置,當檔案已經存在是就會進行覆蓋,如果覆蓋掉的檔案是 so、dex 或者 odex 檔案,就有可能造成嚴重的安全問題。
正例:

對路徑進行判斷,存在".."時丟擲異常。
//對重要的 Zip 壓縮包檔案進行數字簽名校驗,校驗通過才進行解壓
String entryName = entry.getName();
if (entryName.contains("..")){
	throw new Exception("unsecurity zipfile!");
}
複製程式碼

反例:

BufferedOutputStream dest = null;
try {
		ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream("/Users/yunmogong/Documents/test/test.zip")));
		ZipEntry entry;
		while ((entry = zis.getNextEntry()) != null){
			int count;
			byte data[] = new byte[BUFFER];
			String entryName = entry.getName();
			FileOutputStream fos = new FileOutputStream(entryName);
			//System.out.println("Extracting:" + entry);
			dest = new BufferedOutputStream(fos, BUFFER);
			while ((count=zis.read(data,0,BUFFER)) != -1){
				dest.write(data, 0, count);
			}
			dest.flush();
		}
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		try {
			dest.close();
		} catch (IOException e) {
			e.printStackTrace();
	}
}
複製程式碼

如上程式碼,沒有對檔案的路徑名進行判斷直接進行解壓,如果路徑中包含../字串,就會造成目錄的遍歷問題,一旦遭到中間人攻擊替換下載的檔案,將會導致某些惡意檔案被執行。
24、【強制】開放的 activity/service/receiver 等需要對傳入的 intent 做合法性校驗。
25、【推薦】加密演算法:使用不安全的 Hash 演算法(MD5/SHA-1)加密資訊,存在被破解的風險,建議使用 SHA-256 等安全性更高的 Hash 演算法。
26、【推薦】Android WebView 元件載入網頁發生證照認證錯誤時,採用預設的處理方法handler.cancel(),停止載入問題頁面。
說明:
Android WebView 元件載入網頁發生證照認證錯誤時,會呼叫 WebViewClient 類的onReceivedSslError 方法,如果該方法實現呼叫了 handler.proceed()來忽略該證照錯誤,則會受到中間人攻擊的威脅,可能導致隱私洩露.
反例:

mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new JsBridge(mContext), JS_OBJECT);
mWebView.loadUrl("http://www.example.org/tests/addjsif/");
mWebView.setWebViewClient(new WebViewClient() {
	@Override
	public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
		handler.proceed(); // 忽略 SSL 證照錯誤
	}
});
複製程式碼

27、【推薦】直接傳遞命令字或者間接處理有敏感資訊或操作時,避免使用 socket 實現, 使用能夠控制許可權校驗身份的方式通訊。

其他

1、【強制】不要通過 Msg 傳遞大的物件,會導致記憶體問題。 2、【強制】不能使用 System.out.println 列印 log。 正例:

Log.d(TAG, "Some Android Debug info ...");
複製程式碼

反例:

System.out.println("System out println ...");
複製程式碼

3、【強制】Log 的 tag 不能是" "。 說明: 日誌的 tag 是空字串沒有任何意義,也不利於過濾日誌。 正例:

private static String TAG = "LoginActivity";
Log.e(TAG, "Login failed!");
複製程式碼

反例:

Log.e("", "Login failed!");
複製程式碼

阿里Android開發規範:資原始檔命名與使用規範
阿里Android開發規範:四大基本元件
阿里Android開發規範:UI 與佈局
阿里Android開發規範:程式、執行緒與訊息通訊
阿里Android開發規範:檔案與資料庫
阿里Android開發規範:Bitmap、Drawable 與動畫
阿里Android開發規範:安全與其他

相關文章