六、困难问题

原书:Android Application Secure Design/Secure Coding Guidebook

译者:飞龙

协议:CC BY-NC-SA 4.0

在 Android 中,由于 Android 操作系统规范或 Android 操作系统提供的功能,难以确保应用实现的安全性。 这些功能被恶意第三方滥用或用户不小心使用,始终存在可能导致信息泄露等安全问题的风险。 本章通过指出开发人员可以针对这些功能采取的风险缓解计划,将一些需要引起注意的主题挑选为文章。

6.1 来自剪贴板的信息泄露风险

复制和粘贴是用户经常以不经意的方式使用的功能。 例如,不少用户使用这些功能来存储好奇或重要的信息,将邮件或网页中的东西记到记事本中,或者从存储密码的记事本复制并粘贴密码,以便不会提前忘记。 这些明显非常随意的行为,但实际上存在用户处理的信息可能被盗的隐藏风险。

这个风险与 Android 系统中的复制粘贴机制有关。 用户或应用复制的信息,曾经存储在称为剪贴板的缓冲区中。 存储在剪贴板中的信息,在被用户或应用粘贴时,分发给其他应用。 所以这个剪贴板功能中存在导致信息泄漏的风险。 这是因为剪贴板的实体在系统中是唯一的,并且任何应用都可以使用ClipboardManager,随时获取存储在剪贴板中的信息。 这意味着用户复制/剪切的所有信息都会泄露给恶意应用。

因此,考虑到 Android 操作系统的规范,应用开发人员需要采取措施,尽量减少信息泄露的可能性。

6.1.1 示例代码

粗略地说,有两种对策用于减轻来自剪贴板的信息泄露风险

  1. 从其他应用复制到你的应用时采取对策。
  2. 从你的应用复制到其他应用时采取对策。

首先,让我们讨论上面的对策(1)。 假设用户从其他应用(如记事本,Web 浏览器或邮件应用)复制字符串,然后将其粘贴到你的应用的EditText中。 事实证明,在这种情况下,基本没有对策,来防止由于复制和粘贴而导致的敏感信息泄漏。 由于 Android 中没有功能来控制第三方应用的复制操作。 因此,就对策(1)而言,除了向用户解释复制和粘贴敏感信息的风险外,没有任何方法,只能继续让用户自行减少操作。

接下来的讨论是上面的对策(2),假设用户复制应用中显示的敏感信息。 在这种情况下,防止泄漏的有效对策是,禁止来自视图(TextViewEditText等)的复制/剪切操作。 如果输入/输出敏感信息(如个人信息)的视图中,没有复制/剪切功能,信息泄漏永远不会通过剪贴板在你的应用发生。

有几种禁止复制/剪切的方法。 本节介绍简单有效的方法:一种方法是禁用视图的长按,另一种方法是在选择字符串时从菜单中删除复制/剪切条目。

对策的必要性可以根据图 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中的字符串选择菜单中删除复制/剪切条目。

要点:

  1. 从字符串选择菜单中删除android.R.id.copy
  2. 从字符串选择菜单中删除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;@Overridepublic 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;}};@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.uncopyable, menu);return true;}@Overridepublic 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文件中规定。

要点:

  1. 在视图中将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;@Overridepublic IBinder onBind(Intent arg0) {return null;}@Overridepublic 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();}}@Overridepublic 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";@Overridepublic 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()方法将覆盖存储在剪贴板中的信息,因此用户的复制/剪切存储的信息可能会丢失。 当使用这些方法提供自定义复制/剪切功能时,必须按需设计/实现,以防止存储在剪贴板中的内容改变为意外内容,通过显示对话框来通知内容将被改变。

安卓应用安全指南 六、困难问题相关推荐

  1. 安卓应用安全指南 4.4.3 创建/使用服务高级话题

    安卓应用安全指南 4.4.3 创建/使用服务高级话题 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC ...

  2. C++11 并发指南六(atomic 类型详解四 C 风格原子操作介绍)

    前面三篇文章<C++11 并发指南六(atomic 类型详解一 atomic_flag 介绍)>.<C++11 并发指南六( <atomic> 类型详解二 std::at ...

  3. C++11 并发指南六(atomic 类型详解三 std::atomic (续))

    C++11 并发指南六( <atomic> 类型详解二 std::atomic ) 介绍了基本的原子类型 std::atomic 的用法,本节我会给大家介绍C++11 标准库中的 std: ...

  4. C++11 并发指南六( atomic 类型详解二 std::atomic )

    C++11 并发指南六(atomic 类型详解一 atomic_flag 介绍)  一文介绍了 C++11 中最简单的原子类型 std::atomic_flag,但是 std::atomic_flag ...

  5. 安卓应用安全指南 4.6.3 处理文件 高级话题

    安卓应用安全指南 4.6.3 处理文件 高级话题 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY ...

  6. 安卓应用安全指南 4.6.2 处理文件 规则书

    安卓应用安全指南 4.6.2 处理文件 规则书 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY- ...

  7. 安卓应用安全指南 4.6.1 处理文件 示例代码

    安卓应用安全指南 4.6.1 处理文件 示例代码 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY ...

  8. 安卓应用安全指南 4.5.2 使用 SQLite 规则书

    安卓应用安全指南 4.5.2 使用 SQLite 规则书 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:C ...

  9. 安卓应用安全指南 4.5.1 使用 SQLite 示例代码

    安卓应用安全指南 4.5.1 使用 SQLite 示例代码 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议: ...

最新文章

  1. php敏感字符串过滤_PHP实现的敏感词过滤方法示例
  2. SAP R3 FI 上线 顾问设置教程(一)
  3. ASP.Net请求小周期
  4. spring beanFactory
  5. 【CodeForces - 798D】Mike and distribution (思维构造,贪心,黑科技)
  6. 减少HTTP请求(大型网站优化技术)
  7. 使用三级缓存解决内存溢出
  8. python中set函数_python中set()函数简介及实例解析
  9. 丹麦为NSA开绿灯 通过通信中心监控欧洲政客
  10. 三个参数之和、积的取值范围问题
  11. 干货!一文带您读懂区块链溯源!
  12. nginx+lua_nginx+GraphicsMagick生成实时缩略图
  13. python 直播源_直播源获取软件下载|直播源获取工具(斗鱼B站西瓜)下载-蛙扑下载站...
  14. MTK 6765/6739/6755/6761/6763 android9.0 user版本打开root权限(adb root权限和 apk root权限)
  15. HTTP协议的工作原理
  16. 客户信息管理系统(java)
  17. Java2022面试题集锦
  18. 以下哪一个不属于python语言的特点-智慧树知到《Python程序设计基础》章节测试答案...
  19. html5妇女节游戏,三八妇女节趣味小游戏
  20. 小册上新 | 区块链开发入门:从 0 到 1 构建基于以太坊智能合约的 ICO DApp

热门文章

  1. (06)VHDL实现计数器
  2. python正则表达式findall_正则表达式 re.findall 用法
  3. vscode 注释插件
  4. 开关管三极管和MOS管的选择
  5. 关于静态成员变量的谈论
  6. 6003.mavlink协议自定义消息编程
  7. gperf linux 安装_Gperftools安装
  8. 【STM32】关于BOOT引脚和一键下载电路下载的一些事
  9. linux dns resolver,如何解决Linux in-kernel dns_resolver问题
  10. 【线性代数】——投影矩阵