如何让你的Android应用更安全

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 作为Android应用开发者,开发出一款用户喜欢、满意的应用是一件值得开心和满足的事情,而在功能强大的基础上,如何保证应用更安全也显得尤为重要。根据实际开发过程,我们将从以下几个最佳实践来让我们的应用“更加安全”。

如何让你的Android应用更安全

​ 作为Android应用开发者,开发出一款用户喜欢、满意的应用是一件值得开心和满足的事情,而在功能强大的基础上,如何保证应用更安全也显得尤为重要。根据实际开发过程,我们将从以下几个最佳实践来让我们的应用“更加安全”。

1.避免暴露不必要的组件(android:exported属性)

​ android:exported是Android中四大组件Activity,Service,Provider,Receiver中都会有的一个属性,用于表示是否支持其他应用调用当前组件,即android:exported=true表示当前组件可以被其他App使用,而android:exported=false则表示当前组件仅支持在应用内部(当前App)使用。

exported属性的默认值四大组件略有不同:

  • Activity/Service/Receiver:若设置了intent filter,则默认为true,否则为false。
  • Provider:当Android sdk版本为16或更低版本时,默认值为true,如果是17及以上版本则默认为false。

所以我们需要根据自己的需求来进行设定android:exported属性,一般来讲,有如下三种情况:

  • 不暴露组件:在此情况下组件仅限于同一App使用,如果未设置intent-filter,可以不设置exported属性(默认为false);若设置了intent-filter,则必须设置exported=false;
  • 部分暴露组件:此情况下说明组件需要被部分“特定”App调用,则除了需要满足上述“内部使用”外,推荐调用的App与当前暴露组件的App使用同一uid:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
             ...
              android:sharedUserId="xxx.xxx.xxx">

或通过对暴露的组件设置permission:

<activity android:name=".xxxActivity"
          android:label="自定义permission"
          android:permission="com.xxx.permission" >

    <intent-filter>
        <action android:name="android.xxx.action" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

以上两种方式既可满足暴露组件的需求,又可以保护数据安全。

  • 完全暴露组件:即组件可以被任何App调用,可手动设置exported=true(或设置intent-filter),但需要注意针对暴露组件接收的Intent进行异常捕获,避免出现其他App传入异常Intent数据导致出现拒绝服务或crash。

##### 结论

针对四大组件,需要根据实际需求来针对android:exported属性进行设定,避免过分暴露组件导致敏感操作或钓鱼欺骗。

2.避免WebView使用漏洞

​ 目前越来越多的App采用Hybrid方式进行开发,通过使用WebView组件来实现native与Js交互,而近年来爆出的WebView相关漏洞也是层出不穷,所以我们在使用WebView时要注意以下几点:

2.1 避免addJavascriptInterface接口引起远程代码执行漏洞

原因

由于JS可以通过addJavascriptInterface接口调用Android对象:

webView.addJavascriptInterface(new JSObject(), "myObj");
// 参数1:Android的本地对象
// 参数2:JS的对象
// 通过对象映射将Android中的本地对象和JS中的对象进行关联,从而实现JS调用Android的对象和方法

所以当JS拿到Android对象后,就可以调用Android对象中所有的方法,包括系统类(java.lang.Runtime 类),从而进行任意代码执行。

解决办法
  • API >= 17:Google 在 API 17(Android 4.2) 中规定允许被调用的函数必须以@JavascriptInterface进行注解,进而避免漏洞攻击。
  • API < 17:需要采用拦截prompt()的方式进行漏洞修复。

2.2 searchBoxJavaBridge_、accessibility及accessibilityTraversal接口引起的远程代码执行漏洞

原因
  • 在Android 3.0以下,Android系统会默认通过searchBoxJavaBridge_的Js接口给 WebView 添加一个JS映射对象:searchBoxJavaBridge_对象
  • 该接口可能被利用,实现远程任意代码。
解决办法
webview.removeJavascriptInterface("searchBoxJavaBridge_");
webview.removeJavascriptInterface("accessibility");
webview.removeJavascriptInterface("accessibilityTraversal");

2.3 WebView域控制不严格漏洞

原因
  • setAllowFileAccess:Android中默认webView.getSettings().setAllowFileAccess(true),在file域下,能够执行任意的JavaScript代码,同源策略跨域访问能够对私有目录文件进行访问等。
  • setAllowFileAccessFromFileURLs:在JELLY_BEAN(API=16)以前的版本默认是webView.getSettings().setAllowFileAccessFromFileURLs(true),允许通过file域url中的Javascript读取其他本地文件,在JELLY_BEAN及以后的版本中默认已被禁止。
  • setAllowUniversalAccessFromFileURLs:在JELLY_BEAN以前的版本默认是webView.getSettings().setAllowUniversalAccessFromFileURLs(true),允许通过file域url中的Javascript访问其他的源,包括其他的本地文件和http,https源的数据。在JELLY_BEAN及以后的版本中默认已被禁止。
解决办法

通过以下设置,防止越权访问,跨域等安全问题:

setAllowFileAccess(false);
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);

2.4 WebView密码明文存储漏洞

原因

WebView默认开启密码保存功能,如果该功能未关闭,在用户输入密码时,会弹出提示框,询问用户是否保存密码,如果选择"是",密码会被明文保到 /data/data/com.package.name/databases/webview.db

解决办法
webView.setSavePassword(false)

3.使用Https进行网络请求

3.1 HTTPS 简介

HTTP协议是没有加密的明文传输协议,如果我们的应用使用HTTP传输数据,则会泄漏传输的内容,很容易被中间人劫持,修改传输内容。

HTTPS 全称 HTTP over SSL/TLS。SSL/TLS是在传输层上层的协议,应用层的下层,作为一个安全套接层而存在,我们一般叫做传输层安全协议。

tls_

对 HTTP 而言,安全传输层是透明不可见的,应用层仅仅当做使用普通的 Socket一样使用 SSLSocket 。

TLS是基于 X.509 认证,他假定所有的数字证书都是由一个层次化的数字证书认证机构发出,即CA。另外值得一提的是 TLS 是独立于 HTTP 的,任何应用层的协议都可以基于 TLS 建立安全的传输通道,如 SSH 协议。

3.2 数字证书与证书链

数字证书就是互联网通讯中标志通讯各方身份信息的一串数字,它是由权威机构——CA机构,又称为证书授权(Certificate Authority)中心发行的,而保障安全的公钥存储在数字证书中,此证书将用户的身份跟公钥链接在一起。CA必须保证其签发的每个证书的用户身份是唯一的。

链接关系(证书链)通过注册和发布过程创建,取决于担保级别,链接关系可能由CA的各种软件或在人为监督下完成。PKI的确定链接关系的这一角色称为注册管理中心(RA,也称中级证书颁发机构或者中间机构)。RA确保公钥和个人身份链接,可以防抵赖。如果没有RA,CA的Root 证书遭到破坏或者泄露,由此CA颁发的其他证书就全部失去了安全性,所以现在主流的商业数字证书机构CA一般都是提供三级证书,Root 证书签发中级RA证书,由RA证书签发用户使用的证书。

Web 浏览器已预先配置了一组浏览器自动信任的根 CA 证书。来自其他证书授权机构的所有证书都必须附带证书链,以检验这些证书的有效性。证书链是由一系列 CA 证书发出的证书序列,最终以根 CA 证书结束。

验证证书大致过程:HTTPS建立连接后,CA下发给客户端的证书是分层的证书链,要验证某一层证书是否确实由上一级CA发放需要验证附带在该证书上的数字签名(由上级CA通过签名函数及私钥生成的数字签名),数字签名的解密需要上级CA的公钥,这个公钥保存在证书链中上层证书中。而根证书是自己给自己签名,也就是根证书的签名是用自己保存的公钥来解密。这样就保证了最底层的网站证书确实是证书中标明的CA发放的。只有根证书可信,下级证书才可信。

3.3 使用HTTPS通信

我们以HttpURLConnection为例来做说明:

httpsrequest111

如果使用系统默认的SSL,那么就是假设一切CA都是可信的,可往往并非所有CA都可信,所以最好还是实用自定义信任策略。而SSL在校验时是通过X509ExtendedTrustManager进行校验的,即X509TrustManager:

public interface X509TrustManager extends TrustManager {
    public void checkClientTrusted(X509Certificate[] chain, String authType)
            throws CertificateException;

    public void checkServerTrusted(X509Certificate[] chain, String authType)
            throws CertificateException;

    public X509Certificate[] getAcceptedIssuers();
}

最终校验服务器证书的过程会在checkServerTrusted方法中,如果校验没通过会抛出CertificateException,而此处需要特别注意:如果checkServerTrusted方法中什么都不做,则默认信任任何证书,无任何安全性可言,也就失去了校验的意义!千万不要这么做!

那么校验证书有两种方式:

直接通过TrustManager校验证书:

getssl3

通过设置制定信任的证书锚点,仅对该证书及其签发的证书才会被信任:

getssl2

以上就是使用HTTPS进行网络通信的一般过程。针对WebView中使用HTTPS的情况,此处不做更多说明,请查询相关资料。对于网络通信方面,通过https进行通信,将在很大程度上,提升我们应用通信的安全性。

4.增加应用被反编译(二次打包)的难度

APK被反编译是目前作为应用开发者比较头疼的一件事,我们虽然无法做到绝对的安全(不被反编译),但可以通过相关技术手段增加应用被反编译的门槛。

4.1 应用加固

笼统的说APP加固是指对原本容易暴露的程序和运行逻辑进行一定程度的保护,而不影响程序本身的运行结果。其本质就是在二进制程序中植入代码(加固),在运行时优先去的程序控制权做一些额外工作(去壳)。

APP加固一般分为:dex加固so加固,由于dex反编译后的java代码可读性更强,所以目前普遍优先针对dex文件进行加密。

简单来说dex加固基本步骤为:将需要加密的Apk和自己的壳程序Apk,用加密算法对源Apk进行加密再将壳Apk进行合并得到新的Dex文件,最后替换壳程序中的dex文件即可,得到新的Apk,那么这个新的Apk我们也叫作脱壳程序Apk.它已经不是一个完整意义上的Apk程序了,他的主要工作是:负责解密源Apk.然后加载Apk,让其正常运行起来。关于加固有一篇不错的文章可以参考:Android中的Apk的加固(加壳)原理解析和实现

加固后的APP可以有效减少被反编译后二次打包,造成仿冒应用,被不法分子增加恶意代码后重新打包,造成影响厂商利益,威胁用户信息安全的行为。

4.2 混淆

如果你不想开源你的应用,那么在应用发布前,就需要对代码进行混淆处理,这样即使应用被反编译,也难以阅读。混淆除可以增大反编译难度外,还具备减少应用体积、移除未被使用的类和成员以及优化字节码执行的功能。目前常用的混淆器包括ProGuardDexGuard以及DexProtector

ProGuard是Android Studio自带的代码混淆工具。在创建新的Android Studio工程时会自动生成一个ProGuard配置文件。在工程中以proguard-rules.pro命名。

开启ProGuard:

android {
  ...
  buildTypes {
      release {
          minifyEnabled true
          proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
      }
  }
}

注意proguard-rules.pro中,针对不需要进行混淆的类进行keep操作,避免程序运行异常,篇幅原因,更多资料,请自行查阅。

还有很多方面可以提高APP的安全等级,如存储加密,妥善保存应用签名等。安全无小事,作为应用开发者,应当把APP安全与功能开发放在同等地位,尽量在应用上线前通过各种安全平台对应用进行静态扫描及漏洞扫描,考虑使用加固平台对应用进行加固,用以提高APP的安全性。本文内容如有偏颇,欢迎大家指正。

参考资料:

目录
相关文章
|
2月前
|
IDE Java 开发工具
深入探索安卓应用开发:从环境搭建到第一个"Hello, World!"应用
本文将引导读者完成安卓应用开发的初步入门,包括安装必要的开发工具、配置开发环境、创建第一个简单的安卓项目,以及解释其背后的一些基本概念。通过一步步的指导和解释,本文旨在为安卓开发新手提供一个清晰、易懂的起点,帮助读者顺利地迈出安卓开发的第一步。
224 65
|
2月前
|
存储 Java Android开发
探索安卓应用开发:构建你的第一个"Hello World"应用
【9月更文挑战第24天】在本文中,我们将踏上一段激动人心的旅程,深入安卓应用开发的奥秘。通过一个简单而经典的“Hello World”项目,我们将解锁安卓应用开发的基础概念和步骤。无论你是编程新手还是希望扩展技能的老手,这篇文章都将为你提供一次实操体验。从搭建开发环境到运行你的应用,每一步都清晰易懂,确保你能顺利地迈出安卓开发的第一步。让我们开始吧,探索如何将一行简单的代码转变为一个功能齐全的安卓应用!
|
20天前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!
|
20天前
|
存储 搜索推荐 Java
打造个性化安卓应用:从设计到实现
【10月更文挑战第30天】在数字化时代,拥有一个个性化的安卓应用不仅能够提升用户体验,还能加强品牌识别度。本文将引导您了解如何从零开始设计和实现一个安卓应用,涵盖用户界面设计、功能开发和性能优化等关键环节。我们将以一个简单的记事本应用为例,展示如何通过Android Studio工具和Java语言实现基本功能,同时确保应用流畅运行。无论您是初学者还是希望提升现有技能的开发者,这篇文章都将为您提供宝贵的见解和实用的技巧。
|
24天前
|
搜索推荐 开发工具 Android开发
打造个性化Android应用:从设计到实现的旅程
【10月更文挑战第26天】在这个数字时代,拥有一个能够脱颖而出的移动应用是成功的关键。本文将引导您了解如何从概念化阶段出发,通过设计、开发直至发布,一步步构建一个既美观又实用的Android应用。我们将探讨用户体验(UX)设计的重要性,介绍Android开发的核心组件,并通过实际案例展示如何克服开发中的挑战。无论您是初学者还是有经验的开发者,这篇文章都将为您提供宝贵的见解和实用的技巧,帮助您在竞争激烈的应用市场中脱颖而出。
|
26天前
|
算法 Java 数据库
Android 应用的主线程在什么情况下会被阻塞?
【10月更文挑战第20天】为了避免主线程阻塞,我们需要合理地设计和优化应用的代码。将耗时操作移到后台线程执行,使用异步任务、线程池等技术来提高应用的并发处理能力。同时,要注意避免出现死循环、不合理的锁使用等问题。通过这些措施,可以确保主线程能够高效地运行,提供流畅的用户体验。
38 2
|
30天前
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
48 5
|
30天前
|
移动开发 Dart 搜索推荐
打造个性化安卓应用:从零开始的Flutter之旅
【10月更文挑战第20天】本文将引导你开启Flutter开发之旅,通过简单易懂的语言和步骤,让你了解如何从零开始构建一个安卓应用。我们将一起探索Flutter的魅力,实现快速开发,并见证代码示例如何生动地转化为用户界面。无论你是编程新手还是希望扩展技能的开发者,这篇文章都将为你提供价值。
|
1月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
49 4
|
1月前
|
编解码 Android开发 UED
构建高效Android应用:从内存优化到用户体验
【10月更文挑战第11天】本文探讨了如何通过内存优化和用户体验改进来构建高效的Android应用。介绍了使用弱引用来减少内存占用、懒加载资源以降低启动时内存消耗、利用Kotlin协程进行异步处理以保持UI流畅,以及采用响应式设计适配不同屏幕尺寸等具体技术手段。
51 2