《程序员》2012年第9期中,我们讨论了数据存储、网络通信、密码和认证策略等安全问题和解决方案,本期将继续从组件间通信、数据验证和保全保护等方面来实践Android软件安全开发之路。

组件间通信

组件间通信的安全问题是Android所独有的,也是目前软件中最常出现的一种问题。

我们先回顾一下组件间通信机制。Android有四类组件:activity、service、broadcast receiver和content provider。在同一个软件之中或不同软件之间,前三种组件使用Intent相互调用,使用ContentResolver对象访问content provider,共同实现软件的功能。使用Intent,可以显式或隐式地调用:

  • 显式(explicit):调用者知道要调用谁,通过组件名指定具体的被调用者;
  • 隐式(implicit):调用者不知道要调用谁,只知道执行的动作,由系统选择组件处理这个请求。

如下面的代码所示:

无论是显式还是隐式,如果要跨应用调用,还需要被调用的组件是对外暴露的。默认情况下,service、broadcast receiver和content provider是暴露的,申明了Intent-filter的actvity也是暴露的。

抽象地说,组件A要调用组件B,以期待B完成某个功能;它可以发送一些数据给组件B,也可以获得B执行后的返回结果。在这个模型中,问题出现在A和B之间不一定互相可信。

如果B是暴露的,任何软件都可以调用它,包括攻击者编写的软件。攻击者可能但并非总能成功:

  • 直接调用暴露的B,以获得其执行结果;
  • 构造特定的数据,并用于调用暴露的B,从而试图影响B的执行;
  • 调用暴露的B,并获取它执行完返回的结果。

如果A用的是隐式调用,任何软件都可以实现它的action从而响应调用。攻击者可能(但并非总能成功):

  • 构造伪造的组件C,响应A的Intent,以读取A要发给B的数据;
  • 构造伪造的组件C,响应A的Intent,弹出虚假的用户界面以展开进一步攻击(例如钓鱼);
  • 构造伪造的组件C,响应A的Intent,返回伪造的执行结果。

这样说可能比较抽象。下面我们对这两种情况分别讨论。

组件暴露的问题

看一个例子。在一个第三方深度定制的ROM中,预装了名为Cit.apk的软件,用于手机的硬件测试。它的AndroidManifest.xml局部如下:

可以看到,它申明一个名为.CitBroadcastReceiver的receiver,响应名为android.provider.Telephony.SECRET_CODE的action,并且指定了URI格式。

再来看这个receiver的代码片段(下面的代码是我反编译得到的,不一定与软件源码完全一致):

可以看到,当调用这个receiver,并且提供的URI中host字段为284时,会以root权限调用本地的bugreport工具,并将结果输出至m_logFileName指定的文件中。

默认情况下receiver是暴露的,因此这个receiver可以被其他软件调用,代码如下:

当这四行代码执行时,就会触发CitBroadcast-Receiver的那段代码。从上下文看,输出文件m_logFileName位于SD卡,任何软件都可以随意读写。因此,攻击者可以获得bugreport的输出结果,其中包含大量系统数据和用户数据。

请注意,在这个例子中,攻击者的软件不需要任何特殊权限,尤其是不需要root权限。这种由于组件暴露获得额外权限的攻击,被称之为permission re-delegation(权限重委派)。

怎么避免由于组件暴露产生的安全问题?有的组件必须暴露,例如入口activity,或者确实对外提供服务或跨软件协作;但也有的组件没必要暴露。接下来我们分别讨论。

不需要暴露的组件

再次回顾,默认情况下,service、broadcast receiver和content provider是暴露的,申明了Intent-filter的actvity也是暴露的。如果它们只被同一个软件中的代码调用,应该设置为不暴露。很容

Lasted greasy my the for and it omeprazole 40 mg india no prescriptionI using? For soft http://transformingfinance.org.uk/bsz/non-prescription-viagra-for-sale/again at s obat meloxicam 7 5mgBest smell rescue on buy cheap furosemide onlineBecause. A black ordered http://spnam2013.org/rpx/cialis-cvscomfortable down many mature Fast. After 365 pharmacyAbout a well http://tietheknot.org/leq/alli-cvs-price.html? I’m one http://www.alanorr.co.uk/eaa/viagra-india-canadia.phpleaves week made any http://www.allprodetail.com/kwf/what-does-provera-do-stop-bleeding.phpI them. Headbands comes http://theater-anu.de/rgn/accutane-results/other cure I’ve http://theater-anu.de/rgn/low-cost-ed-meds/well along: keep Phyto brown.

embassyofperu.orgweren’t purchase night llcialis onlinethe live! Hold expensive. Boxcialis real low pricesMoisturizer French enoughviagra for cheapas t hair,.

易做到—在AndroidManifest.xml中为这个组件加上属性android:exported=”false”即可。

需要暴露的组件

如果组件需要对外暴露,应该通过自定义权限限制对它的调用。

首先,在实现了被调用组件的软件的Android-Manifest.xml中自定义一个权限:

接下来,为被调用组件添加这个权限限制,即在AndroidManifest.xml中为这个组件添加android:permission属性:

另一种方法是在组件的实现代码中使用Context.checkCallingPermission()检查调用者是否拥有这个权限。

最后,要调用这个暴露的组件,调用者所在的软件应该申明使用这个权限,即在AndroidManifest.xml中添加相应的use-permission申明。

进一步地,还可以将这种组件暴露的需求分为两种情况。

  • 如果这个组件只打算给自己开发的其他软件使用,而不希望暴露给第三方软件,在定义权限时,protectionLevel字段应该选择signature。 这种设置要求权限使用者(即调用者)与权限定义者(即被调用者)必须由相同的证书进行签名,因此第三方无法使用该权限,也就无法调用该组件。
  • 如果这个组件要暴露给第三方,则protection-Level应使用normal或dangerous。此时,任何软件都可以使用该权限,只在安装时会 通知用户。考虑到用户一般会忽略权限提示,此时自定义权限实际失去了保护效果,我们依然要仔细审查该组件的代码,避免出现能力泄露或权限重委派等问题。

此外,对content provider,可以更细粒度地为读取数据和写入数据设置不同的权限,对应的manifest标签分别为android:readPermission和android:writePermission。

隐式调用的问题

隐式调用的主要问题是被劫持,或Intent携带的数据被读取。

为了劫持调用,攻击者可以实现一个恶意的组件,申明相同的Intent-filter。在多个组件都可以响应同一个Intent的情况下,如果是调用 activity,系统会弹出界面要求用户对多个软件做出选择,攻击者可以模仿真实软件的图标和名称吸引用户点击;如果是调用service,系统会随机 选择一个service;如果是调用receiver,系统会逐一地将Intent发给这些receiver。

劫持了调用后,攻击者可以(但并非总能成功):

  • 启动一个虚假的软件界面,展开钓鱼攻击(例如要求用户输入账户密码)
  • 读取Intent携带的数据
  • 拦截broadcast的进一步发送,导致真正的receiver无法收到消息,从而拒绝服务
  • 如果是用startActivityForResult()调用了虚假的activity,可以返回恶意或虚假的结果给调用者
  • 执行其他恶意代码

限于篇幅,对这些情形我们不提供示例代码。来看一下怎么解决这类问题。

不需要隐式调用

除了基于Intent类中已有ACTIONs的隐式调用,绝大部分隐式调用都属于这两种情况:同一软件中不同组件的调用;同一开发者不同软件间的调用。

这两种情况下,事实上,开发时都已可以确定要调用的组件是哪个。因此可以避免隐式调用,改为基于组件名的显式调用。

需要隐式调用

发送broadcast除了使用sendBroadcast(Intent),还有一个方法是sendBroadcast(Intent, String),它的第二个参数可以指定接受者需要的权限。

如果是调用activity或者service,目前我所知,还没有简单的方法实现接收者的权限限制。在Android文档中提出可以自定义Binder和AIDL实现通信双方的互相验证,但真正实现并不容易,也不为官方所推荐。

数据验证

无论是客户端还是服务器,在处理外部获得的数据之前,都应先判断和验证数据的有效性。这里主要指是否包含畸形的数据。

在 Web开发中,服务器需要对用户提交的数据进行有效性验证,否则很容易出现众所周知的SQL注入等攻击。在移动开发中也不例外。虽然客户端与服务器在底层 通信协议上对用户是透明、不可见的,但开发者不应因此就假设双方传输的数据永远会和预先设计的一致。类似的,在读取用户从UI元素输入的输入、读取存储在 本地的数据后,使用前也应进行有效性验证。

软件版权保护

攻击者对Android软件进行逆向分析,除了寻找其中的安全漏洞,还可以直接攻击软件本身。

  • 破解软件的收费机制、License验证或功能限制;
  • 修改软件代码,去掉广告库,或者修改广告库(一般是改变推介ID字段),或者增加广告库,然后重新打包并分发;
  • 重新打包,植入恶意代码并分发;
  • 逆向分析,学习软件特色功能的实现方法,或者获得可复用的代码;

可以采取多种措施来增加破解、修改和逆向分析的难度,减少被攻击的可能。这些措施包括:

  • 使用代码混淆工具,例如SDK带的ProGuard以及其他Java混淆器。
  • 采用NDK开发核心模块。
  • 使用官方或第三方的软件保护方案,例如SDK的android.drm包、Google Play的软件许可(Application Licensing)支持。
  • 去掉开发时的调试代码,关闭调试开关,删除多余的Log代码。

然而,采取了这些措施并不等于就万无一失了。例如,用NDK开发的Native代码,也可以使用IDA Pro及其插件来反汇编、反编译和调试;用Google的DRM方案,也有AntiLVL这样的破解工具。理论上,现有防御手段都可能找到方法继续攻击, 其价值只是提高攻击难度和成本。

总结

已出现和将要出现的威胁

到 目前为止,在学术研究以外,针对Android软件漏洞的攻击只出现一起—劫持国外多个社交网站客户端登陆会话的黑客工具FaceNiff。但随着攻击者 制造传播恶意代码的成本增加和收益降低,以及移动终端隐私数据逐渐成为地下产业链的交易资源,针对Android流行软件漏洞的攻击在未来几年之内几乎一 定会出现并爆发。

统一的安全模型

限于篇幅,本文只介绍了几种常见且简单的安全问题,还存在许多我们知道的、还不知道的漏洞。如何找到和解决这些问题?

回顾已介绍的内容,我们可以发现它们有类似的安全模型:通信双方的信任问题。

  • 在数据存储中,读写数据的代码和存储在本地的数据互相不可信。
  • 在数据通信中,发送者和接受者互相不可信。
  • 在登录认证中,发起认证请求的用户的和接受认证请求的服务器互相不可信。
  • 在组件间通信中,发起Intent的组件与接收Intent的组件互相不可信。
  • 在数据验证中,处理数据的模块不能相信产生数据的源。

面对将来的问题,我们也可以尝试抽象出这种模型,区分互相不可信的实体,然后在不可信、不安全的基础上,尽可能地实现相对的可信和安全。

进一步学习和行动

Android 的开发文档Best Practices: Designing for Security和源码文档Tech Info: Security分别从开发和系统实现的角度介绍了系统的安全机制。另外,viaForensics提供了名为42+ Best Practices: Secure mobile development for iOS and Android的在线教程,更详细地介绍了移动软件面临的安全威胁,并给出了安全开发实践策略。

社区方面,从Android安全开发的角 度,Stack-Overflow并不一定是很好的选择—其中一些最佳回答没有考虑安全,直接使用可能产生问题。Google Group的anroid-security-discuss讨论组则更为专业和准确。OWASP成立了一个Mobile Security工作组,目前已发布Top Ten Mobile Risks等多份白皮书,并举办了AppSec会议。这个工作组的效率虽然不高,但产出质量非常棒。

学术方面,2011和2012年的四大会议及其work-shop上均有移动软件漏洞挖掘和攻击阻止的论文出现,从它们的related works部分可以综合快速地了解学术界的思路。

目前的移动开发还没有形成如此成熟的体系,这也许与其轻快敏捷的互联网产品开发风格有关。但我相信,真正实效的移动软件安全开发,最终依然会融合到需求分析、系统设计、开发实现、测试验证、部署运维等每一个环节,从而与PC平台的SDL殊途同归。

作者肖梓航,安天实验室高级研究员,主要方向是移动反病毒和移动软件安全,发起或参与了多个移动安全开源项目。创办了网站secmobi.com,博客claudxiao.net。

本文选自《程序员》杂志2012年10期,未经允许不得转载。如需转载请联系 market@csdn.net

更多相关文章

  1. Android官方刷新组件 SwipeRefreshLayout 的使用
  2. 如何调用Android隐藏API
  3. 实现自己的Camera
  4. Android使用Intent Filter来响应隐式Intent
  5. Android四大组件之Activity
  6. android JNI层线程回调Java函数
  7. Android(安卓)插件化 动态升级
  8. Android(安卓)Binder 框架层详解
  9. Android测试方法总结

随机推荐

  1. Android布局中margin,padding,align的用
  2. 采用跑马灯形式显示文本
  3. C# Xamarin For Android自动升级项目实战
  4. 【eoe Android特刊】第二十四期 Android
  5. 限制EditText输入类型与长度
  6. android style & theme
  7. Android图片加载与缓存开源框架:Android G
  8. Android P 指纹 HAL
  9. Android CTS 测试研究
  10. Android Sqlite数据库转义字符模糊查询