安卓應用安全指南六、困難問題
六、困難問題
原書:Android Application Secure Design/Secure Coding Guidebook
譯者:飛龍
在 Android 中,由於 Android 作業系統規範或 Android 作業系統提供的功能,難以確保應用實現的安全性。 這些功能被惡意第三方濫用或使用者不小心使用,始終存在可能導致資訊洩露等安全問題的風險。 本章通過指出開發人員可以針對這些功能採取的風險緩解計劃,將一些需要引起注意的主題挑選為文章。
6.1 來自剪貼簿的資訊洩露風險
複製和貼上是使用者經常以不經意的方式使用的功能。 例如,不少使用者使用這些功能來儲存好奇或重要的資訊,將郵件或網頁中的東西記到記事本中,或者從儲存密碼的記事本複製並貼上密碼,以便不會提前忘記。 這些明顯非常隨意的行為,但實際上存在使用者處理的資訊可能被盜的隱藏風險。
這個風險與 Android 系統中的複製貼上機制有關。 使用者或應用複製的資訊,曾經儲存在稱為剪貼簿的緩衝區中。 儲存在剪貼簿中的資訊,在被使用者或應用貼上時,分發給其他應用。 所以這個剪貼簿功能中存在導致資訊洩漏的風險。 這是因為剪貼簿的實體在系統中是唯一的,並且任何應用都可以使用ClipboardManager
,隨時獲取儲存在剪貼簿中的資訊。 這意味著使用者複製/剪下的所有資訊都會洩露給惡意應用。
因此,考慮到 Android 作業系統的規範,應用開發人員需要採取措施,儘量減少資訊洩露的可能性。
6.1.1 示例程式碼
粗略地說,有兩種對策用於減輕來自剪貼簿的資訊洩露風險
- 從其他應用複製到你的應用時採取對策。
- 從你的應用複製到其他應用時採取對策。
首先,讓我們討論上面的對策(1)。 假設使用者從其他應用(如記事本,Web 瀏覽器或郵件應用)複製字串,然後將其貼上到你的應用的EditText
中。 事實證明,在這種情況下,基本沒有對策,來防止由於複製和貼上而導致的敏感資訊洩漏。 由於 Android 中沒有功能來控制第三方應用的複製操作。 因此,就對策(1)而言,除了向使用者解釋複製和貼上敏感資訊的風險外,沒有任何方法,只能繼續讓使用者自行減少操作。
接下來的討論是上面的對策(2),假設使用者複製應用中顯示的敏感資訊。 在這種情況下,防止洩漏的有效對策是,禁止來自檢視(TextView
,EditText
等)的複製/剪下操作。 如果輸入/輸出敏感資訊(如個人資訊)的檢視中,沒有複製/剪下功能,資訊洩漏永遠不會通過剪貼簿在你的應用發生。
有幾種禁止複製/剪下的方法。 本節介紹簡單有效的方法:一種方法是禁用檢視的長按,另一種方法是在選擇字串時從選單中刪除複製/剪下條目。
對策的必要性可以根據圖 6.1-1 的流程確定。 在圖 6.1-1 中,“輸入型別固定為密碼屬性”表示,輸入型別在應用執行時必須是以下三種之一。 在這種情況下,由於預設禁止複製/剪下,因此不需要採取對策。
InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD
InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD
InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD
以下小節使用每個示例程式碼詳細介紹了對策。
6.1.1.1 選擇字串時,從選單中刪除複製/剪下條目
在 Android 3.0(API Level 11)之前不能使用TextView.setCustomSelectionActionMODECallback()
方法。 在這種情況下,禁止複製/剪下的最簡單方法是禁用檢視的長按。 禁用檢視的長按可以在layout.xml
檔案中規定。
下面展示了示例程式碼,用於從EditText
中的字串選擇選單中刪除複製/剪下條目。
要點:
- 從字串選擇選單中刪除
android.R.id.copy
。 - 從字串選擇選單中刪除
android.R.id.cut
。
UncopyableActivity.java
package org.jssec.android.clipboard.leakage;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.NavUtils;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
public class UncopyableActivity extends Activity {
private EditText copyableEdit;
private EditText uncopyableEdit;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.uncopyable);
copyableEdit = (EditText) findViewById(R.id.copyable_edit);
uncopyableEdit = (EditText) findViewById(R.id.uncopyable_edit);
// By setCustomSelectionActionMODECallback method,
// Possible to customize menu of character string selection.
uncopyableEdit.setCustomSelectionActionModeCallback(actionModeCallback);
}
private ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
public void onDestroyActionMode(ActionMode mode) {
}
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// *** POINT 1 *** Delete android.R.id.copy from the menu of character string selection.
MenuItem itemCopy = menu.findItem(android.R.id.copy);
if (itemCopy != null) {
menu.removeItem(android.R.id.copy);
}
// *** POINT 2 *** Delete android.R.id.cut from the menu of character string selection.
MenuItem itemCut = menu.findItem(android.R.id.cut);
if (itemCut != null) {
menu.removeItem(android.R.id.cut);
}
return true;
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
};
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.uncopyable, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
}
6.1.1.2 禁用檢視的長按
禁止複製/剪下也可以通過禁用檢視的長按來實現。 禁用檢視的長按可以在layout.xml
檔案中規定。
要點:
- 在檢視中將
android:longClickable
設定為false
,來禁止複製/剪下。
unlongclickable.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/unlongclickable_description" />
<!-- EditText to prohibit copy/cut EditText -->
<!-- *** POINT 1 *** Set false to android:longClickable in View to prohibit copy/cut. -->
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:longClickable="false"
android:hint="@string/unlongclickable_hint" />
</LinearLayout>
6.1.2 規則書
將敏感資訊從你的應用複製到其他應用時,請遵循以下規則:
6.1.2.1 禁用檢視中顯示的複製/剪下字串(必需)
如果應用中存在顯示敏感資訊的檢視,並且允許在檢視中像EditText
一樣複製/剪下資訊,資訊可能會通過剪貼簿洩漏。 因此,必須在顯示敏感資訊的檢視中禁用複製/剪下。 有兩種方法禁用複製/剪下。 一種方法是從字串選擇選單中刪除複製/剪下條目,另一種方法是禁用檢視的長按。 請參閱“6.1.3.1 應用規則時的注意事項”。
6.1.3 高階話題
6.1.3.1 應用規則時的注意事項
在TextView
中,選擇字串是不可能的,因此通常不需要對策,但在某些情況下,可以複製取決於應用的規範。選擇/複製字串的可能性可以通過使用TextView.setTextIsSelectable()
方法動態決定。將TextView
設定為可以複製時,應調查在TextView
中顯示任何敏感資訊的可能性,並且如果有任何可能性,則不應將其設定為可複製的。
另外,在“6.1.1 示例程式碼”的決策流程中描述,根據EditText
的輸入型別(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD
等),假設輸入型別是密碼,通常不需要任何對策,因為複製字串是預設禁止的。但是,如“5.1.2.2 提供以明文顯示密碼的選項(必需)”中所述,如果準備了【以明文顯示密碼】的選項,則在以明文顯示密碼的情況下,輸入型別將會改變,並且啟用複製/剪下。因此應該要求採取同樣的對策。
請注意,開發者在應用規則時,還應考慮到應用的可用性。 例如,在使用者可以自由輸入文字的檢視的情況下,如果因輸入敏感資訊的可能性很小而禁用了複製/剪下,使用者可能會感到不便。 當然,該規則應該無條件地,應用於處理非常重要的資訊或獨立的敏感資訊的檢視,但在檢視之外的情況下,以下問題將幫助開發人員瞭解如何正確處理檢視。
- 準備一些專門用於敏感資訊的其他元件
- 當嚮應用的貼上是顯而易見的時候,用其他方法傳送資訊
- 提醒使用者注意輸入/輸出資訊
- 重新審視檢視的必要性
資訊洩露風險的根源在於,Android 作業系統中剪貼簿和剪貼簿管理器的規範不考慮安全風險。 應用開發人員需要在使用者完整性,可用性,功能等方面建立更高質量的應用。
6.1.3.2 儲存在剪貼簿中的操作資訊
正如“6.1 來自剪貼簿的資訊洩漏風險”中所述,應用可以使用ClipboardManager
,操作儲存在剪貼簿中的資訊。另外,不需要為使用ClipboardManager
設定特定的許可權,因此應用可以在不被使用者識別的情況下,使用ClipboardManager
。
儲存在剪貼簿中的資訊稱為ClipData
,可以通過ClipboardManager.getPrimaryClip()
方法獲得。如果通過ClipboardManager.addPrimaryClipChangedListener()
方法,將偵聽器註冊到ClipboardManager
,並實現了OnPrimaryClipChangedListener
,則每次使用者執行復制/剪下操作時都會呼叫監聽器。因此可以在不忽略時間的情況下獲得ClipData
。在任何應用中執行復制/剪下操作時,都會呼叫監聽器。
下面顯示了服務的原始碼,無論什麼時候在裝置中執行復制/剪下,它都會獲取ClipData
並通過Toast
顯示。你可以意識到,儲存在剪貼簿中的資訊被洩露出來,就是由於下面的簡單程式碼。有必要注意,敏感資訊至少不會由以下原始碼使用。
ClipboardListeningService.java
package org.jssec.android.clipboard;
import android.app.Service;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ClipboardManager.OnPrimaryClipChangedListener;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
public class ClipboardListeningService extends Service {
private static final String TAG = "ClipboardListeningService";
private ClipboardManager mClipboardManager;
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
mClipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
if (mClipboardManager != null) {
mClipboardManager.addPrimaryClipChangedListener(clipListener);
} else {
Log.e(TAG, "Failed to get ClipboardService . Service is closed.");
this.stopSelf();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mClipboardManager != null) {
mClipboardManager.removePrimaryClipChangedListener(clipListener);
}
}
private OnPrimaryClipChangedListener clipListener = new OnPrimaryClipChangedListener() {
public void onPrimaryClipChanged() {
if (mClipboardManager != null && mClipboardManager.hasPrimaryClip()) {
ClipData data = mClipboardManager.getPrimaryClip();
ClipData.Item item = data.getItemAt(0);
Toast.makeText(
getApplicationContext(),
"Character stirng that is copied or cut:¥n"
+ item.coerceToText(getApplicationContext()),
Toast.LENGTH_SHORT)
.show();
}
}
};
}
接下來,下面顯示了Activity
的示例程式碼,它使用上面涉及的ClipboardListeningService
。
ClipboardListeningActivity.java
package org.jssec.android.clipboard;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class ClipboardListeningActivity extends Activity {
private static final String TAG = "ClipboardListeningActivity";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_clipboard_listening);
}
public void onClickStartService(View view) {
if (view.getId() != R.id.start_service_button) {
Log.w(TAG, "View ID is incorrect.");
} else {
ComponentName cn = startService(
new Intent(ClipboardListeningActivity.this, ClipboardListeningService.class));
if (cn == null) {
Log.e(TAG, "Failed to launch the service.");
}
}
}
public void onClickStopService(View view) {
if (view.getId() != R.id.stop_service_button) {
Log.w(TAG, "View ID is incorrect.");
} else {
stopService(new Intent(ClipboardListeningActivity.this, ClipboardListeningService.class));
}
}
}
到目前為止,我們已經介紹了獲取儲存在剪貼簿上的資料的方法。 也可以使用ClipboardManager.setPrimaryClip()
方法在剪貼簿上儲存新資料。
請注意,setPrimaryClip()
方法將覆蓋儲存在剪貼簿中的資訊,因此使用者的複製/剪下儲存的資訊可能會丟失。 當使用這些方法提供自定義複製/剪下功能時,必須按需設計/實現,以防止儲存在剪貼簿中的內容改變為意外內容,通過顯示對話方塊來通知內容將被改變。
相關文章
- 安卓應用安全指南翻譯完成安卓
- 安卓應用安全指南4.8輸出到LogCat安卓GC
- 安卓應用安全指南4.5.2使用SQLite規則書安卓SQLite
- 安卓應用安全指南4.2.3建立/使用廣播接收器高階話題安卓
- 安卓應用安全指南4.1.2建立/使用活動規則書安卓
- 安卓應用安全指南 5.6.1 密碼學 示例程式碼安卓密碼學
- 安卓應用安全指南4.7使用可瀏覽的意圖安卓
- 安卓應用安全指南4.1.1建立/使用活動示例程式碼安卓
- 安卓應用安全指南4.6.1處理檔案示例程式碼安卓
- 安卓應用安全指南4.3.1建立/使用內容供應器示例程式碼安卓
- 安卓應用安全指南5.5.2處理隱私資料規則書安卓
- 如何解決MES交付困難問題?
- 安卓應用安全指南5.4.1通過HTTPS的通訊示例程式碼安卓HTTP
- Anbox安卓apk應用安裝及使用說明和常見問題安卓APK
- 一些困難題
- 研發團隊溝通困難 誰的問題?
- ctfshow_web_1(困難題)Web
- appium 安卓應用指令碼APP安卓指令碼
- Flutter 如何釋出安卓應用?Flutter安卓
- 安卓 Surfaceview 的截圖的問題安卓View
- [LeetCode 困難 動態規劃+LIS問題]354. 俄羅斯套娃信封問題LeetCode動態規劃
- 安卓權威指南第三版第16章拍照閃退問題安卓
- 丰采網愛採購徵集企業營銷困難問題WSW
- 7款最佳安卓日曆應用安卓
- App上架應用市場,如何攻破安全過檢難題APP
- 安卓stdio中新建activity遇到的問題安卓
- 安卓so包常見報錯問題安卓
- 出門困難
- 大資料應用需注意哪些安全問題大資料
- 安卓移動應用程式碼安全加固系統設計及實現安卓
- Leetcode171-190刷題筆記(非困難題)LeetCode筆記
- 幽默:程式設計中困難的不是解決問題,而是確定要解決的問題 - Paul程式設計
- 2024.07.27科大訊飛(有困難題)
- 徹底理解安卓應用無響應機制安卓
- 為什麼實施物聯網安全非常困難
- 破解Oracle應用整合難題RWOracle
- [奇技Y巧]解決程式快取鎖命名困難,命名衝突問題快取
- Android應用安全常見問題及解決方案Android