安卓应用安全指南 六、困难问题
六、困难问题
原书: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()
方法将覆盖存储在剪贴板中的信息,因此用户的复制/剪切存储的信息可能会丢失。 当使用这些方法提供自定义复制/剪切功能时,必须按需设计/实现,以防止存储在剪贴板中的内容改变为意外内容,通过显示对话框来通知内容将被改变。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
安卓应用安全指南 5.6.3 密码学 高级话题
5.6.3 密码学 高级话题 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA 4.0 5.6.3.1 选择加密方法 在上面的示例代码中,我们展示了三种加密方法的实现示例,每种加密方法用于加密解密以及数据伪造的检测。 你可以使用“图 5.6-1”,“图 5.6-2”,根据你的应用粗略选择使用哪种加密方法。 另一方面,加密方法的更加精细的选择,需要更详细地比较各种方法的特征。 在下面我们考虑一些这样的比较。 用于加密和解密的密码学方法的比较 公钥密码术具有很高的处理成本,因此不适合大规模数据处理。但是,因为用于加密和解密的密钥不同,所以仅仅在应用侧处理公钥(即,只执行加密),并且在不同(安全)位置执行解密的情况下,管理密钥相对容易。共享密钥加密是一种通用的加密方案,但限制很少,但在这种情况下,相同的密钥用于加密和解密,因此有必要将密钥安全地存储在应用中,从而使密钥管理变得困难。基于密码的密钥系统(基于密码的共享密钥系统)通过用户指定的密码生成密钥,避免了在设备中存储密钥相关的...
- 下一篇
大牛直播SDK: 如何实现简单粗暴靠谱的直播抓娃娃方案
市面上的娃娃机方案五花八门,daniulive认为最简单靠谱粗暴性价比的方案如下: RTMP摄像机–>CDN–>daniulive player(专门针对直播娃娃机开通了“超低延迟”模式,公网实际延迟在200~400毫秒). 娃娃机抓取过程录制方案: 行业内首屈一指的录制方案,利用大牛直播播放端SDK,支持两个摄像头切换过程中(SmartPlayerSwitchUrl)录制到同一个MP4文件,便于用户分享抓取过程或进行申诉。 娃娃机视频管理方案: 利用大牛直播的Windows播放器,同时多窗口播放rtmp摄像机数据,采用轮询播放的方式,有设备发生重连或断线之类,实时统计或者告警,保证前端rtmp摄像机的正常运转。 相关播放器下载: Windows平台下载 Android平台下载 iOS平台下载 比快更快,效果轻松PK市面上“全球第一、国际领先”的各类技术方案。 大牛直播精英群: 294891451 大牛直播技术交流群: 499687479 最近接到最大的质疑是:为什么只有大牛直播推荐这种rtmp摄像头方案? 回答:我们不是做云服务的,通俗点说,我们不是靠带宽盈利的,我们期望...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8安装Docker,最新的服务器搭配容器使用
- 设置Eclipse缩进为4个空格,增强代码规范
- CentOS7安装Docker,走上虚拟化容器引擎之路