移动应用安全
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,例如:
xml
<activity
android:name=".MySecureActivity"
android:permission="com.example.MY_PERMISSION">
</activity>
在这个示例中,MySecureActivity 只有在其他应用声明并持有 com.example.MY_PERMISSION 权限时才可以启动。需要在 AndroidManifest.xml 文件中定义这个权限,如下所示:
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,可以视为潜在的调试行为。
java
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>
来判断是否被调试
java
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),可以检测到截屏操作
例如:
java
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 用于监听内容提供者中的数据变化。通过监听系统媒体数据库中的资源变化(如媒体库),可以检测到截图文件的新增
例如:
java
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>
中进行检测或采取保护措施
例子:
java
@Override
protected void onPause() {
super.onPause();
// 当 Activity 失去焦点时,尝试检测是否存在截屏
handleScreenshot();
}
++4、使用 View 层面的保护措施++
在应用的 View 层面,尤其是在敏感数据的界面,可以通过设置 <font style="color:#0e0e0e;">FLAG_SECURE</font>
来防止内容被截图。这会阻止系统截屏和其他屏幕录制工具捕获该 Activity 的内容
java
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);