移动应用安全
移动应用安全
- 1.APP开发安全
- AndroidManifest配置安全
- Activity组件安全
- Service组件安全
- Provider组件安全
- BroadcastReceiver组件安全
- WebView组件安全
- 2.APP业务安全
- 代码安全
- 数据安全
- 3.其他
- 安全输入键盘
- 防截屏
1.APP开发安全
AndroidManifest配置安全
每个Android应用的根目录中都必须包含一个AndroidManifest.xml
文件。Manifest文件为Android系统提供有关应用的基本信息,系统必须获得这些信息才能运行任意应用代码
此外,Manifest文件还可执行以下操作:
- 为应用的Java软件包命名。软件包名称充当应用的唯一标识符
- 描述应用的各个组件,包括构成应用的Activity、服务、广播接收器和内容提供程序。为实现每个组件的类命名并发布其功能。根据这些声明,Android系统可以了解这组件具体是什么,以及在什么条件下可以启动它们
- 确定将托管应用组件的进程
- 声明应用必须具备哪些权限才能访问API中受保护的部分并与其他应用交互
- 声明其他应用与该应用组件交互所需具备的权限
- 列出Instrumentation类,这些类可在应用运行期间提供分析和其他信息。这些声明只会在应用处在开发和测试阶段时出现在清单文件中,在应用发布之前会被删除
- 声明应用所需的最低Android API级别
- 列出应用必须链接的库
我们所熟知的导出组件,是Android上最常见也是门槛最低的攻击入口,如Manifest中组件设置不当的话,就存在被任意调用的可能。此外,在Manifest配置文件中,还有一些可被调试的程序、可被导出的应用数据以及与Scheme相关的配置开关,一旦开启就会存在一些风险
Manifest配置不当类风险与对策:
漏洞或风险 | 产生原因 | 后果 | 解决方案 |
---|---|---|---|
程序可被任意调试 | AndroidManifest.xml配置错误,android:debuggable=“true”,调试开关被打开 | APP可以被调试 | 修改AndroidManifest.xml配置为android:debuggable=“false” |
允许程序数据备份 | AndroidManifest.xml中allowBackup=“true”,数据备份开关被打开 | 程序数据任意备份,应用数据可被导出 | 修改AndroidManifest.xml配置为allowBackup=“false”,并使用适当的保护级别(如android:protectionLevel=“signature”)验证调用来源 |
Activity组件暴露 | Activity组件的exported属性被设置为true,或未设置exported但IntentFilter不为空,activity被认为是导出的 | Activity组件可被外部访问,实施越权攻击 | 在AndroidManifest.xml中明确设置Activity组件的exported属性为false,若需与其他APP共享或交互数据,则进行权限控制和参数校验 |
Service组件暴露 | Service组件的exported属性被设置为true,或未设置exported但IntentFilter不为空,service被认为是导出的 | Service组件可被外部访问,实施越权攻击 | 同上 |
ContentProvider组件暴露 | ContentProvider的exported属性被设置为true,或Android API<=16时默认导出 | 越权访问应用本身不想共享的数据 | 同上 |
BroadcastReceiver组件暴露 | BroadcastReceiver的exported属性被设置为true,或未设置exported但IntentFilter不为空,BroadcastReceiver被认为是导出的 | BroadcastReceiver可被外部恶意调用,导致数据泄漏或越权访问 | 同上 |
Intent Scheme URLs 攻击 | 在 AndroidManifest.xml设置 Scheme 协议之后,可以通过浏览器打开对应的 Activity | 访问浏览器构造intent 语法即可唤起App相应组件,导致拒绝服务甚至越权行为 | 配置 category filter,添加android.intent.category.BROWSABLE方式规避风险 |
Activity组件安全
在Android开发中,Activity 是四大组件之一(包括 Activity、Service、BroadcastReceiver 和 ContentProvider),它负责用户界面和用户交互。由于 Activity 是用户直接交互的组件(用户唯一能够看见的组件),所以其安全性非常重要。
1、组件导出暴露问题
Activity 可以通过 AndroidManifest.xml 文件中的 android:exported 属性来控制是否允许其他应用调用。如果不希望其他应用启动你的 Activity,应将 android:exported 属性设置为 false
2、访问权限控制
为了保护 Activity 不被未经授权的应用访问,你可以使用 android:permission 属性来指定一个权限字符串,只有具有该权限的应用才能启动这个 Activity,例如:
<activity
android:name=".MySecureActivity"
android:permission="com.example.MY_PERMISSION">
</activity>
在这个示例中,MySecureActivity 只有在其他应用声明并持有 com.example.MY_PERMISSION 权限时才可以启动。需要在 AndroidManifest.xml 文件中定义这个权限,如下所示:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<permission
android:name="com.example.MY_PERMISSION"
android:protectionLevel="signature" />
<application>
<activity
android:name=".MySecureActivity"
android:permission="com.example.MY_PERMISSION">
</activity>
</application>
</manifest>
3、劫持问题
在启动Activity时,加入标志位FLAG_ACTIVITY_NEW_TASK,就能使该Activity置于栈顶立即呈现给用户。恶意软件可以监控目标Activity,侦测到目标Activity启动后,立即弹出一个与该应用界面相同的 Activity,实现伪装目标Activity,也就是我们所说的“被劫持”问题
针对Activity劫持目前没有特别好的办法彻底解决,一个思路是在APP一些关键界面(比如登录界面)被覆盖时弹出一些提示信息,进入后台的时候判断是不是用户自己触发,如果不是也弹出提示信息。
Service组件安全
Service组件是Android系统中的后台进程,主要的功能是在后台进行一些耗时的操作。
建议私有Service不定义intent-filter并且设置exported为false,需要被同公司不同APP访问时,可以将protectionLevel设置为signature;如果是合作伙伴APP访问,需要对其APP签名做校验。若存在Service返回数据的情况,则需要关注敏感信息泄露风险。
Provider组件安全
Content Provider组件是Android应用的重要组件之一,管理对数据的访问,主要用于不同的应用程序之间实现数据共享。Content Provider的数据源不止包括SQLite数据库,还可以是文件数据。通过将数据储存层和应用层分离,Content Provider为各种数据源提供了一个通用的接口。
Content Provider组件允许其他应用通过 URI来访问应用的数据,如果在AndroidManifest文件中将某个ContentProvider的exported属性设置为true,就会产生一些越权访问数据、SQL注入、目录遍历等风险。例如:
1、私有权限定义错误导致数据被任意访问
私有权限定义经常发生的风险是:定义了私有权限,但是根本没有定义私有权限的级别,或者定义的权限级别不够,导致恶意应用只要声明这个权限就能够访问相应的Content Provider提供的数据,造成数据泄露
2、本地SQL注入漏洞
Content Provider的query()
如果使用拼接字符串组成的SQL语句去查询底层的SQLite数据库时,容易发生SQL注入
3、目录遍历漏洞
对外暴露的Content Provider实现了OpenFile()
接口。如果没有进行Content Provider访问权限控制和对访问的目标文件的URI进行有效判断,攻击者利用“../
”实现目录遍历便可访问任意可读文件。更有甚者,在Openfile()
接口的实现中,如果要访问的文件不存在,就会创建此文件,也就是说还有可能往手机设备可写目录中写入任意数据。
防御手段:
- 由于API level在17以下的所有应用的“android:exported”属性默认值都为true,因此如果应用的ContentProvider不必导出,建议显式设置注册的Content Provider组件的“android:exported”属性为false。
- 如果必须要有数据提供给外部应用,则需要做好权限控制,明确什么样的外部应用可以使用,尽量不要提供用户隐私敏感信息。一般来讲,大部分开放的Provider,都是提供给本公司其他应用使用,一般打包签名APP的签名证书是一致的,这样便可以将Provider的ProtectionLevel设置为signature。如果是合作方的APP来访问,可以将合作方APP的签名哈希值预埋在提供Provider的APP中,提供Provider的APP要检查请求访问此Provider的APP的签名,匹配通过了才能访问
- 为了避免SQL语句,不要使用拼接字符串的形式,可以使用SQLiteDatabase类中的参数化查询
query()
方法。 - 为了防止目录遍历,建议去除Content Provider中的
OpenFile()
接口,对访问的目标文件路径进行有效判断,过滤“../
”等字符串
BroadcastReceiver组件安全
BroadcastReceiver中文被译为广播接收者,用于处理接收到的广播,广播接收者的安全分为接收安全与发送安全两个方面
1、接收安全
动态注册广播如果仅为应用内部使用,应当将exported设置为false,这样外部应用不能随便发送广播到自身程序中。如果需要接收外部应用,则需要配置权限,和前面Provider的一样,如果是本公司其他APP,将ProtectionLevel设置为signature;如果是其他合作伙伴的APP,则除了设置ProtectionLevel外还建议避免敏感信息的传递。
2、发送安全
Android系统提供了两种广播发送方法,即sendOrderedBroadcast
和sendBroadcast
。
有序广播通过Context.sendOrderedBroadcast()
来发送,所有的广播接收器优先级依次执行,广播接收器的优先级通过receiver的intent-filter中的android:priority属性来设置,数值越大优先级越高。当广播接收器接收到广播后,可以使用setResult()函数来将结果传给下一个广播接收器接收,然后通过getResult()函数取得上个广播接收器接收返回的结果。当广播接收器接收到广播后,也可以用abortBroadcast()函数让系统拦截下该广播,并将该广播丢弃,使该广播不再传送到别的广播接收器接收。
普通广播是完全异步的,通过Context的sendBroadcast()
方法来发送,消息传递效率比较高,但所有receivers(接收器)的执行顺序不确定。接收器不能将处理结果传递给下一个接收器,并且无法终止广播 Intent的传播,直到没有与之匹配的广播接收器为止。
Android官方在SDK文档中说明了一些不安全的API,包括:sendStickyBroadcast、sendStickyOrderedBroadcast、sendStickyOrderedBroadcastAsUser、sendStickyBroadcastAsUser,建议不要在 APP使用。
WebView组件安全
WebView是一个基于Webkit引擎、展现Web页面的组件,APP通过调用该组件就可以访问网页内容,所以有非常多的移动应用都内嵌了WebView组件
对黑客而言,它是一个非常理想的攻击面,点开一个链接或者扫描一个二维码就会执行恶意代码。在使用WebView组件过程中,除了一些系统隐藏接口,还会有一些与本地交互、保存密码、HTTPS通信认证相关的风险需要关注,常见WebView风险如下表:
漏洞或风险 | 产生原因 | 后果 | 解决方案 |
---|---|---|---|
<font style="color:#0e0e0e;">addJavascriptInterface</font> 远程代码执行漏洞 | Android 的 WebView 组件提供了一个 <font style="color:#0e0e0e;">addJavascriptInterface</font> 方法,用于实现本地 Java 代码和 JavaScript 代码之间的交互 | 在targetSdkVersion 小于17时(Android 4.2),攻击者利用接口<font style="color:#0e0e0e;">addJavascriptInterface</font> 添加的函数,可以远程执行任意代码 | 禁止使用addJavascriptInterface ,推荐使用 <font style="color:#0e0e0e;">@JavascriptInterface</font> 注解来提高安全性 |
Webview 组件系统隐藏接口未移除漏洞 | 在 Android WebView 组件中,存在一些隐藏的系统接口,如 <font style="color:#0e0e0e;">searchBoxJavaBridge_accessibility</font> , <font style="color:#0e0e0e;">Traversal</font> , 和 <font style="color:#0e0e0e;">accessibility</font> ,这些接口在早期的 Android 版本中未被适当地限制,从而可能导致远程代码执行的安全风险 | 使用 Android WebView 组件时,如果没有移除这3个导出接口,可能导致远程代码任意执行 | 使用 <font style="color:#0e0e0e;">WebView.removeJavascriptInterface(String name)</font> 方法显式地移除隐藏系统接口 |
通过 JavaScript 调用 getClassLoader 方法,实现远程代码执行 | 在低于 API 17 的版本中,在 Context 子类中使用 <font style="color:#0e0e0e;">addJavascriptInterface</font> 方法将 this 对象(即当前 Context 对象)绑定到 WebView 上 | 调用 getClassLoader,绕过 Google 对 getClass 方法的安全限制,从而执行任意代码 | <font style="color:#0e0e0e;">targetSdkVersion</font> 使用大于17的版本 |
Webview密码明文保存漏洞 | WebView 可能会将用户输入的敏感信息(如用户名和密码)明文保存在应用数据目录下的 <font style="color:#0e0e0e;">databases/webview.db</font> 文件中 | 用户密码信息泄露风险 | 可以使用 WebSettings 对象来配置 WebView 的设置。具体来说,你可以调用 <font style="color:#0e0e0e;">WebSettings.setSavePassword(false)</font> 来禁用密码保存功能 |
Webview启动访问文件数据导致用户隐私泄露 | <font style="color:#0e0e0e;">setAllowFileAccess(true)</font> 允许 WebView 访问设备的文件系统,包括应用的私有目录 | 可以执行任意 JavaScript 代码,并有可能绕过同源策略,进而访问私有目录中的文件。导致用户隐私泄露或敏感数据被恶意利用 | 通过调用 <font style="color:#0e0e0e;">WebView.getSettings().setAllowFileAccess(false) </font> 禁止 WebView 访问文件系统 |
2.APP业务安全
代码安全
Android系统的开源以及基于Java的特性,导致APP在Android上更容易被反编译。目前常用的一些反编译工具(比如APKTool、dex2jar等)能够毫不费劲地还原Java里的明文信息,Native里的库信息也可以通过objdump或IDA获取。
而心怀不轨的人可能会通过反编译后加入恶意的代码逻辑,重新打包一个APK文件去发布安装,也就是我们常说的“二次打包”问题。针对这些问题,常见的解决方案是代码混淆、加壳、反调试、签名验证等
1、代码混淆
APP的代码混淆包括Java代码的混淆以及一些资源文件的混淆
ProGuard代码混淆工具,开发人员可以使用它对自己的代码进行保护。ProGuard提供了压缩、混淆、优化代码以及反混淆栈跟踪的功能
资源文件的混淆,一些互联网公司也提供了一些方法供参考,比如微信提供的:
https://github.com/shwenzhang/AndResGuard
下载地址:https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/
2、加壳
Android加壳分为dex加壳和对native编译(即so文件加壳)主流的加壳技术基本可以分为四代:
1️⃣整体dex加壳
在开发过程中,classes.dex 文件会被加密。加密后的 dex 文件通常被存储在 APK 的资源文件中(如 assets 或 res 目录),而不是直接以明文形式包含在 APK 中
当应用运行时,加载一个解壳代码或库,它会在内存中将加密的 dex 文件解密为原始的 classes.dex。此过程不会将解密后的文件直接写入磁盘,而是将解密后的代码存放在内存中
一旦 dex 文件在内存中解密,应用会使用 Dalvik 虚拟机(或 Android 5.0 之后的 ART 虚拟机)通过反射或 ClassLoader 动态加载解密后的代码,从而使应用继续运行
2️⃣防调试防Dump
整体dex在内存中解密,黑客通过内存Dump的方式即可拿到明文,所以出现了防调试、防内存Dump的技术
3️⃣方法体抽离
相比前面整体加壳加密,第三代开始尝试只对classes.dex文件中的方法、函数进行抽取加密,在Java虚拟机执行具体某个方法时才将其动态解密,并以不连续的方式存放到内存中。后面慢慢发展成将Java代码内关键算法、业务逻辑等函数自动转化成native的C++代码进行防护
4️⃣VMP加壳/so加壳
我们知道程序的执行,是依靠CPU对于符合规范的指令集的解析处理。如果将原指令集通过自定义规范进行变形处理,生成新的指令集(称之为虚拟指令集),CPU将无法识别虚拟指令。此时若配合能够解析虚拟指令集的解释器(称之为虚拟机),就可以达到不直接通过CPU而是通过虚拟机来执行虚拟指令。这就是很多公司各种VMP保护方案的基本原理
某厂商网站上的风险描述表:
等级 | 风险描述 | 人群分布 | 建议或措施 |
---|---|---|---|
1 | 使用apktool、dex2jar、jd-gui、baksmali等工具,直接查看Java代码 | 99% | 对DEX文件加密,或将核心代码封装到SO文件中 |
2 | 使用 DexExtractor 等自动脱壳工具,通过内存数据动态提取 Java 代码 | 10% | 采用Java2c技术,将Java代码转化为C代码,增加反编译难度,并进行SO加壳保护 |
3 | 可修复ELF文件,并使用IDA工具反编译还原后的SO文件,分析核心代码逻辑 | 2% | 对SO文件进行代码混淆加密保护 |
4 | 可剔除 SO 文件中的混淆代码,自动化还原原始代码 | 0.5% | 对核心代码进行虚拟化加密保护 |
5 | 可分析自定义 CPU 指令,并自动化还原原始 CPU 指令 | 0.001% | 定制升级虚拟化方案 |
APP加固的目的是提升对手的攻击成本,如果通过加固能让相当一部分人知难而退,就达到APP加固本身的效果了
在实际工作中,考虑到APP内部复杂的业务场景和升级机制,有一些加固方案可能会存在一定的兼容性问题,用户体验要求高的场景可能还会追求运行速度,因此需要慎重权衡。一般的思路是在APP上进行各种埋点,结合后端大数据风控来降低安全风险。
3、反调试
1️⃣使用android.os.Debug.isDebuggerConnected()
这是最常见的 API,用于判断调试器是否已连接到当前进程。如果返回 true,则表示应用正在被调试,可以根据这个信号采取防御措施或终止进程
2️⃣通过检查 <font style="color:#0e0e0e;">ApplicationInfo.FLAG_DEBUGGABLE</font>
属性,可以判断应用是否是以 debug 模式运行。生产环境中的应用一般不会开启此标志位,如果检测到 <font style="color:#0e0e0e;">FLAG_DEBUGGABLE</font>
为 true,可以视为潜在的调试行为。
boolean isDebuggable = (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
3️⃣android_server 是一些调试工具(如 GDB、LLDB)使用的调试服务。如果发现该进程或相关端口正在监听,可能表示当前进程正在被调试。可以通过遍历 <font style="color:#0e0e0e;">/proc/net/tcp</font>
等系统文件检查是否有调试端口开启
4️⃣TracePid 是 Linux 内核中的一个字段,当应用被调试时,该字段会显示调试器的 PID。可以通过循环读取 <font style="color:#0e0e0e;">/proc/self/status</font>
来判断是否被调试
String status = readFile("/proc/self/status");
if (status.contains("TracerPid:\t0")) {
// 没有被调试
} else {
// 被调试
}
5️⃣检测模拟器。模拟器检测技术,一般是取一些模拟器特征,例如通过电话管理器攻取设备IMEI、IMSI,判断设备配置信息与Android模拟器设备配置默认值是否相同,检测设备是否有安装蓝牙设备硬件,判断当前设备WIFI MAC地址,检测是否具有QEMU虚拟机通道文件等。
6️⃣检测设备是否已经ROOT。ROOT检测一般的思路是,看ROOT后的手机会有哪些特征,比如,检测su文件是否存在及可以执行,检测是否安装<font style="color:#0e0e0e;">Superuser.apk</font>
等。但实际情况是,Android碎片化非常严重,国产手机厂商特别喜欢修改原生ROM,这会导致一些检测方法失效,需要关注
4、签名验证
签名验证主要是为了防止二次打包,Android签名验证一般有三种方法:
- Java层验证,即在Java代码中实现公钥信息的比对,比对的样本可以放在本地或者服务器侧。但是 Java代码容易被反编译,这个校验逻辑可能被篡改
- NDK层验证,即在Native代码实现公钥信息的比对,比对的样本进行加密存储。我们知道so通过反汇编生成的ARM代码,相对smali被篡改的难度更大,再结合so文件加固,可以进一步增强反编译难度
- 服务端验证,即程序通过特定方法检测获取自身代码校验值,上送云端服务器进行比较,从而验证合法性。这样针对那些非法或伪造、篡改过的客户端,服务端可以直接拒绝服务,进一步保障安全
数据安全
针对APP,我们通常说的敏感信息包含两方面:
一是用户敏感信息,比如用户名、密码、手机号、邮箱、身份证、银行卡、住址等
二是APP本身的一些敏感信息,包括产品核心算法、核心业务逻辑、私钥、本地存储的证书、加密算法等
这些敏感信息都需要在设计APP时考虑严格保护,在存储、使用、传输过程中也要考虑保护方法
1、数据存储安全
Android有外部存储和内部存储之分,外部存储安全隐患比较大
1️⃣涉及用户隐私哪怕是已经加密过的也不要保存到外部存储设备上,还有一些APP会动态加载一些外部资源,这些外部资源可能保存在外部存储上,建议在加载时验证文件完整性。在做代码审计的时候,关注getExternalStorageState
、getExternalStorageDirectory
等关键函数就能定位到APP使用外部存储的代码逻辑
2️⃣内部存储是所有软件存放私有数据的地方,Android SDK中提供了openFileInput()
与openFileOutput()
方法来读写程序的私有数据目录。openFileOutput()
方法的第二个参数指定了文件创建模式,如果使用了MODE_WORLD_READABLE
或 MODE_WORLD_WRITEABLE
,就可能导致敏感信息泄露
3️⃣除了File方式外,Android还提供了Shared Preference、SQLite、ContentProvider方式进行数据存储。与openFileOutput()
方法一样,Shared Preference的getSharedPreferences方法打开文件时第二个参数如果设置为MODE_WORLD_READABLE
或MODE_WORLD_WRITEABLE
都存在一样的问题,应将第二个参数设置为MODE_PRIVATE
,这样就可以利用Linux的文件权限机制来确保数据不被其他进程访问
单单依靠
MODE_PRIVATE
模式是不够的,因为可能APP运行在一个已经被Root的手机上,或者手机系统出现了一些漏洞导致进程可以提升权限,所以往往敏感数据会采取加密措施
Android SDK提供了一些API供加密使用,这些API和JAVA提供的基本相似,由Java Cryptography Architecture
(JCA,Java加密体系结构)、Java Cryptography Extension
(JCE,Java加密扩展包)、Java Secure Sockets Extension
(JSSE,Java安全套接字扩展包)、Java Authentication andAuthentication Service
(JAAS,Java鉴别与安全服务)组成
- JCA提供基本的加密框架,如证书、数字签名、消息摘要和密钥对产生器等
- JCE扩展了JCA,提供了各种加密算法、摘要算法、密钥管理等功能
- JSSE提供了基于SSL(安全套接层)的加密功能,供HTTPS加密传输使用
- JAAS提供了在Java平台上进行用户身份鉴别的功能
除此外,Android还提供了android.security
和android.security.keystore
来管理keychain和keystore。(Keychain 是用于管理用户证书和私钥的系统级存储机制,允许应用程序通过系统安全接口访问这些凭据;Keystore 是 Android 提供的加密密钥管理系统,允许应用安全地生成、存储和使用加密密钥)
如果使用SQLite数据库的时候需要加密,可以使用SQLCipher方案。SQLCipher是个独立的SQLite数据库实现,但它并没有自己实现一套加密算法,而是使用了OpenSSL的libcrypto库,兼容性更好
4️⃣关于加密算法的选择和使用:
- base64只是一种编码方式,并不是加密算法
- 生成随机数时,不要使用
Random
类,使用SecureRandom
类的时候,不要调用setSeed
方法,即不要设置种子 - 使用HASH算法时,不要使用MD2、MD4、MD5、SHA-1、RIPEMD算法来加密用户密码等敏感信息,因为网上有大量的库可以用来破解,建议使用SHA-256、SHA-3算法
- 使用消息认证算法时,建议使用HMAC-SHA256算法,避免使用CBC-MAC
- 使用对称加密算法时,不建议使用DES,建议使用AES算法,同时需要注意加密模式要显式指定为CBC或CFB模式,不要使用默认的ECB模式
- 非对称算法使用RSA时,建议密钥长度不低于512,同时注意重放攻击
- 使用基于口令的加密算法PBE时,生成密钥时要加盐,盐的取值最好来自SecureRandom,并指定迭代次数
- 白盒加密技术的核心思想是将加密算法和密钥打包成一个不可分割的整体,并生成加密表和解密表。加密和解密操作不再依赖传统的算法执行,而是通过查表的方式完成。这种方式使得即使攻击者能够获取到加密或解密过程,也无法直接从中提取出密钥或还原算法
2、数据传输安全
一般客户端使用HTTPS与服务端进行通信,网站启用SSL site wide(use HTTPS only)或HSTS(HTTP Strict Transport Security),否则存在SSL Strip(HTTPS降级为HTTP)攻击风险
3.其他
安全输入键盘
金融行业APP,密码往往涉及金钱,为了确保输入安全,一些企业会自行开发安全输入键盘
这样当用户输入密码的时候,处于自行开发的密码键盘保护之下,确保用户密码安全。当然除了密码,还有其他的输入数据也需要保护,例如用户的身份证号等敏感数据
防截屏
在 Android 系统中,由于没有直接提供对截屏事件的监听接口或广播,开发者需要依赖间接的检测机制来应对截屏行为,当发现有截屏时做一些处理,比如用纯黑色图片对象进行覆盖处理。
1、利用 FileObserver 监听目录变化
FileObserver 是 Android 提供的一个用于监控文件系统变化的类,可以监听文件或目录的创建、删除、修改等操作。通过监控系统默认的截图保存目录(如 /Pictures/Screenshots),可以检测到截屏操作
例如:
FileObserver screenshotObserver = new FileObserver(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES + "/Screenshots").getPath()) {
@Override
public void onEvent(int event, String file) {
if (event == FileObserver.CREATE) {
// 截屏事件检测到
handleScreenshot();
}
}
};
screenshotObserver.startWatching();
2、利用 ContentObserver 监听全局资源变化
ContentObserver 用于监听内容提供者中的数据变化。通过监听系统媒体数据库中的资源变化(如媒体库),可以检测到截图文件的新增
例如:
ContentObserver mediaObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
// 检查媒体库中是否有新的截屏文件
handleScreenshot();
}
};
getContentResolver().registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, mediaObserver);
3、利用 <u><font style="color:#0e0e0e;">Activity.onPause() </font></u>
机制
<font style="color:#0e0e0e;">onPause()</font>
方法在 Activity 失去焦点时调用。在某些情况下,如果用户截屏时应用失去焦点,可以在 <font style="color:#0e0e0e;">onPause()</font>
中进行检测或采取保护措施
例子:
@Override
protected void onPause() {
super.onPause();
// 当 Activity 失去焦点时,尝试检测是否存在截屏
handleScreenshot();
}
4、使用 View 层面的保护措施
在应用的 View 层面,尤其是在敏感数据的界面,可以通过设置 <font style="color:#0e0e0e;">FLAG_SECURE</font>
来防止内容被截图。这会阻止系统截屏和其他屏幕录制工具捕获该 Activity 的内容
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);