最近公司的同事甩给我一份爱加密的应用安全监测报告,细看了一下,问题还不少,于是乎,我开始调研一些解决方案,本篇主要介绍四大组件的安全漏洞问题调研。
1. Activity最小化特权检测
1.1 问题描述
Activity的最小化特权是指组件只能自身调用,其他应用无权访问,即组件不导出。Activity组件设置导出权限,则该组件能够被外部的其他组件直接调用,这样就可能导致泄露隐私数据或者应用程序崩溃等风险。Activity被恶意应用调用,可能有以下威胁描述:修改程序的状态或者数据;被调用的Activity可能返回隐私信息给恶意应用,造成数据泄露;可能使应用程序崩溃,造成拒绝服务等漏洞。
抽取AndroidManifest.xml文件,扫描所有的Activity组件。
查看组件的EXPORTED属性,发现存在可以导出的组件。
问题代码
<activity
android:name=".xxxActivity"
android:exported="true"
/>
<p style = "color:red">也就是说,这个问题是由于exported属性设置为true导致的</p>
exported属性
总体来说它的主要作用是:是否支持其它应用调用当前组件。
默认值:如果包含有intent-filter 默认值为true; 没有intent-filter默认值为false。
1.2 解决方案
- 设置 Activity 组件 android:exported = false
- 必须 exported 的 Activity 组件必须仅限于授权用户或者特定组件调用
- 谨慎使用 intent-filter 属性,若必须使用,则需强制设置 android:exported = false
1.3 优化流程
爱加固的报告中指出了多处问题代码,下面逐一排查:
- 1) 首先MainActivity中设置了exported=true,根据项目需求,MainActivity不需要被其他应用打开,这是一处问题代码,删掉exported属性,优化完成;
- 2) 具有intent-filter属性的Activity,默认具有exported=true属性(尽管这里没有明文写上),这里的逻辑是网页配置url_scheme协议代码,客户端配置<intent-filter>属性和浏览器打开能力,那么网页具有唤醒App某一页面的能力;
<activity
android:name=".xxx.SettingActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<data
android:host="host1"
android:scheme="scheme1"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
</activity>
不妨测试一下这个功能,首先写一个html文件,配置urlscheme代码:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="minimum-scale=1.0, width=device-width, maximum-scale=1.0, user-scalable=no"/>
<meta charset="utf-8">
<title>Url Scheme Test</title>
</head>
<body>
<h1>Test</h1>
<a style="color: blue" href="scheme1://host1:8888">打开App设置</a>
</body>
</html>
在手机的浏览器中打开它,点击【打开App设置】
Web H5浏览器会弹出这样授权框,确定,正常打开了我们App的设置页
App尝试修改exported=false,再次测试,不弹窗,不打开,显然这与需求相悖,如果业务真实需要网页打开App的某些页面,那么这里是不能优化的.
- 3)exported组件优化
加密报告中给我们的另一个建议是 必须 exported 的 Activity 组件必须仅限于授权用户或者特定组件调用,查阅资料,可以通过自定义安全权限的方式来处理必须的组件调用,从而避免引发Activity最小化权限问题- 首先,在被调用的应用A中添加自定义权限和启动方式
protectionLevel有["normal" | "dangerous" | "signature" | "signatureOrSystem"]等多个可选值, 用于标识该权限的等级,可控制权限的授予方式,normal 表示声明后自动获取,signature 表示定义权限的 APK 和声明权限的 APK 必须使用同一签名文件,才可获取权限。
<permission android:name="dshapp.permission.OPEN_SETTINGS" android:protectionLevel="signature"/> ... <activity android:name=".SettingActivity" android:permission="dshapp.permission.OEPN_SETTINGS"> <intent-filter> <action android:name="dshapp.intent.action.OEPN_SETTINGS"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity>
- 然后的B应用中添加用户权限,并配置成与A相同的签名信息,保证能够正常唤起A应用,否则会引发错误
权限添加
<uses-permission android:name="dshapp.permission.OEPN_SETTINGS"/>
签名设置signingConfigs { debug { storeFile file("dsh_sign.store") storePassword "123456" keyAlias "dsh" keyPassword "123456" } } buildTypes { debug{ signingConfig signingConfigs.debug } }
//Activity启动代码 String SIGN_ACTION = "dshapp.intent.action.OEPN_SETTINGS"; startActivity(new Intent(SIGN_ACTION));
- 最后测试一下,B应用可以唤起A应用的设置页,so,以后面对 多应用相互唤起的需求。可以通过自定义权限的方式来处理
- 首先,在被调用的应用A中添加自定义权限和启动方式
- 4)微信回调WXEntryActivity
根据微信官方文档,当我们需要微信的一些功能时, 我们要为自己应用的WXEntryActivity设置exported=true属性.现在,我将exported修改为false并且进行了测试,如预期那样,当我测试一个微信绑定功能时,回调都无法正常接收了。
那么,自定义一个权限行不行呢,答案是否定的,微信并不认识你自定义的权限,这个只能等微信官方去优化了。
结论:对于微信回调Activity,我的建议是,千万别改,千万别改,千万别改,安全生产责任重于泰山.
同样对于 com.tencent.tauth.AuthActivity类似这种QQ认证页面,也千万不要修改它的exported属性
- 5)友盟微博分享WbShareTransActivity及个推的GActivity
如下:
在个推的清单文件中找到了GActivity声明的代码
<activity
android:name="com.igexin.sdk.GActivity"
android:excludeFromRecents="true"
android:exported="true"
android:process=":pushservice"
android:taskAffinity="com.igexin.sdk.PushActivityTask"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
在友盟分享的清单文件中我找到了WbShareTransActivity声明的代码
<activity
android:name="com.sina.weibo.sdk.share.WbShareTransActivity"
android:launchMode="singleTask"
android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen" >
<intent-filter>
<action android:name="com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
带着不怕死作死的精神,我尝试将SDK中的清单文件复制出来,在app工程中覆盖他们原来的代码,强行exported = false,看看会发生什么,注意,如果不添加tools:replace="android:exported,编译将无法完成
<activity
android:name="com.igexin.sdk.GActivity"
android:excludeFromRecents="true"
android:exported="false"
android:process=":pushservice"
android:taskAffinity="com.igexin.sdk.PushActivityTask"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
tools:replace="android:exported"/>
OK,下面开始测试,如我们所料,端内推送可以正常接收。
端外推送或者厂商推送呢,在应用被杀死的情况下,就不保证能够接收到了,我们来看下关于GActivity的介绍
<p style = "background:#F1F1F1"><strong>com.igexin.sdk.GActivity</strong>
该组件无可视界面,可认为是针对一些后台管理针对启动类型为activity就允许启动而做了个取巧的方式,从而达到可以被其他应用唤醒启动,达到保持<b style= "color:red">正常推送</b>的功能。
该组件主要是维持个推推送服务的后台运行,反正被系统kill.当然主流的手机厂商等后台管理做的好的厂商一般都会识别,即使该组件是activity启动也不允许后台自运行。</p>
可以理解为个推为了保证推送的到达率,允许GActivity通过Service的方式被唤醒,其实这也符合我们对于自己开发应用的期待,更高的推送达到率.
所以这段代码呢,同样我的建议是不要修改、不要修改、不要修改
不管是GActivity还是WbShareTransActivity,他们被注册在各自的SDK文件中,尽管我们可以暴力地去覆盖它原来的属性,但是最好不要这么做,在不熟悉其内部原理的情况下(很遗憾,GAactivity被混淆了),未知的坑点可能使我们修改付出代价。
2.Service最小化特权检测
共五处:
GetuiPushService:个推推送
OppoPushService:oppo厂商推送
HmsMsgService:华为厂商推送
NotificationService:魅族厂商推送
PushMessageHandler:小米厂商推送
这些Service配置在个推及厂商推送SDK的清单文件下,不要要开发者处理,比如华为厂商推送
<!-- 接⼊入HMSSDK PUSH模块需要注册该service,不不需要开发者处理理 -->
<service
android:name="com.huawei.hms.support.api.push.service.HmsMsgService"
android:enabled="true"
android:exported="true"
android:process=":pushservice" >
<intent-filter>
<action android:name="com.huawei.push.msg.NOTIFY_MSG" />
<action android:name="com.huawei.push.msg.PASSBY_MSG" />
</intent-filter>
</service>
3. Broadcast Receiver最小化特权检测
共五处,与Service的五处相对应
PushReceiver:个推推送
HmsPushSubReceiver:华为厂商推送
SystemReceiver:魅族厂商推送
FlymePushReceiver:魅族厂商推送
MiuiPushReceiver:小米厂商推送
4.Content Provider
未发现异常
总结
四大组件特权化问题分别是指四大组件的最小化特权是指组件只能自身调用,其他应用无权访问,即组件不导出。
具体为:
- Aactivity: Activity的Activity组件设置导出权限,则该组件能够被外部的其他组件直接调用,这样就可能导致泄露隐私数据或者应用程序崩溃等风险。Activity被恶意应用调用,可能有以下威胁描述:修改程序的状态或者数据;被调用的Activity可能返回隐私信息给恶意应用,造成数据泄露;可能使应用程序崩溃,造成拒绝服务等漏洞。
- Service:Service执行的操作比较敏感,比如更新数据库、提供事件通知等,如果设置了导出权限,可能被系统或者第三方的App直接调出并使用。Service导出可能导致拒绝服务攻击,程序功能被第三方恶意调用等风险。
- Content Provider:Content Provider组件对外导出后可被第三方恶意程序调用,会产生本地SQL注入、文件遍历等风险,造成用户敏感信息泄露。
- 从全局考虑Broadcast Receiver可以方便应用程序和系统、应用程序之间、应用程序内的通信,对单个应用程序而言Broadcast Receiver是存在安全性问题的,比如恶意程序可以不断的去发送你所接收的广播,这样会造成应用被攻击,导致应用直接退出、处理逻辑出错等风险。
解决思路
- 设置组件 android:exported = false
- 必须 exported 的组件必须仅限于授权用户或者特定组件调用
- 谨慎使用 intent-filter 属性,若必须使用,则需强制设置 android:exported = false
具体优化遵循以下原则:
- 对于业务中不需要导出的组件,修改exported = false
- 对于具有intent-filter属性的组件,exported 默认为 true,如果不需要导出它,请修改exported = false
- 对于web页跳转App,必须保留true属性的,建议只保留一个通用跳转配置页,这符合最小化特权的原则
- 对于业务中必须设置exported=true的组件,必须限制于授权用户或者特定应用组件,参考1.3-3中的自定义用户权限的方式优化;
- 对于三方的SDK,如微信、友盟、个推等,需要遵循开发文档集成规范,不要在自己的清单文件中修改他们,以避免功能异常.
- 及时清理项目中不再需要的组件,尤其是可导出的组件
参考资料:
https://www.jianshu.com/p/3792cf8de23f
https://blog.csdn.net/su749520/article/details/89283184
https://blog.csdn.net/u013293125
网友评论