Android 阿里内部机密资料 安全机制 反编译与混淆 加密原理

一.数据存储安全问题

二.数据传输安全问题

三.Apk被反编译安全问题

四. 组件安全

五.安卓系统本身的安全

 

一.数据存储安全问题

内存缓存在这些场景下非常有用:当你从服务端获取到数据并想将这些数据保存在一段时间内有效,或者你在处理bitmaps 位图时想使用其他地方已经处理好的图片,或者你想保存一些用户数据,用户标识等等。

举个例子,怎么在内存缓存创建一个临时文件:

File outputDir = this.getCacheDir();
File outputFile = File.createTempFile("prefix", "extension", outputDir);

比起在外部存储器中打开一个文件就可以获取到密码,攻击者要dump内存则就难得多。Dump内存需要ROOT权限。
 

 关于数据存储可能出现的问题包括如下几点:外部存储(SD卡)上的文件没有权限管理,所有应用都可读可写。开发者把敏感信息明文存在 SD 卡上,或者动态加载的payload放在SD卡上。

(1)明文存储敏感数据,导致直接被攻击者复制或篡改。

  • 将隐私数据、系统数据明文保存在外部存储
  • 将软件运行时依赖的数据保存在外部存储
  • 将软件安装包或者二进制代码保存在外部存储
  • 使用全局可读写(MODE_WORLD_READABLE,MODE_WORLD_WRITEABLE)的内部存储方式,或明文存储敏感信息(用户账号密码等)。

(2)不恰当存储登陆凭证,导致攻击者利用此数据窃取网络账户隐私数据。

  解决方案:

  • 对这些数据进行加密,密码保存在内部存储,由系统托管或者由用户使用时输入。
  • 对应用配置文件,较安全的方法是保存到内部存储;如果必须存储到SD卡,则应该在每次使用前检验它是否被篡改,与预先保存在内部的文件哈希值进行比较。
  • 应用如果需要安装或加载位于SD卡的任何文件,应该先对其完整性做验证,判断其与实现保存在内部存储中的(或从服务器下载来的)哈希值是否一致。
  • 如果要跨应用进行数据共享,有种较好的方法是实现一个Content Provider 组件,提供数据的读写接口并为读写操作分别设置一个自定义的权限。
  • 对于登录凭证的存储,使用基于凭据而不是密码的协议满足这种资源持久访问的需求,例如OAuth。

Android数据存储机制

       存储方式

描述

数据保密性

Shared preferences

用来存储一些简单配置信息的一种机制,使用Map数据结构来存储数据,以键值对的方式存储,采用了XML格式将数据存储到设备中。例如保存登录用户的用户名和密码。只能在创建它的应用中使用,其他应用无法使用。不能指定存储文件的位置,创建的存储文件保存在/data/data//shares_prefs文件夹下。支持的存储类型有:Boolean、Float、Integer、Long、String等

可以设置四种模式:

MODE_PRIVATE(默认模式,文件只能让创建的应用程序访问)、

MODE_WORLD_WRITEABLE、

MOED_WORLD_READABLE、

MODE_APPEND

内部存储

在设备内存中存储数据。通常这些数据不允许被其它应用甚至终端用户访问。即使重启设备,这些数据仍然存在,不过当终端用户卸载程序后,这些数据就会被删除。

可以设置三种模式:

MODE_PRIVATE(默认模式)、MODE_WORLD_READABLE、

MODE_WORLD_WRITABLE

外部存储

外部存储(SD卡)的数据是全局可读的。设备用户和其它应用都能读取、修改、删除这些数据

数据默认是全局可读的

SQLite数据库

如果需要使用数据库的搜索和数据管理功能,则可以使用SQLite数据库存储机制

程序内部可以任意访问,外部程序不能访问

网络存储

通过web服务器存储和获取数据

基于web 服务器的设置

 

总结:

数据保护这个主要例举以下几点:

  • 不要在客户的存放登录密码(即使你加密了),最好采用token的形式;
  • 数据传输记得加密;
  • 重要数据存放内置存储中,不要存放在外置存储;
  • 加密存放在xml和数据库中的重要信息;

 

二.数据传输安全问题

 

不使用加密传输

  最危险的是直接使用HTTP协议登录账户或交换数据。例如,攻击者在自己设置的钓鱼网络中配置DNS服务器,将软件要连接的服务器域名解析至攻击者的另一台服务器在,这台服务器就可以获得用户登录信息,或者充当客户端与原服务器的中间人,转发双方数据。

  • 使用加密传输但忽略证书验证环节

  如开发者在代码中不检查服务器证书的有效性,或选择接受所有的证书。例如,开发者可以自己实现一个X509TrustManager接口,将其中的CheckServerTrusted()方法实现为空,即不检查服务器是否可信或者在SSLSoketFactory的实例中,通过setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIET),接受所有证书。这可能是因为开发者使用了自己生成的证书,客户端发现证书没有和可信CA 形成信任链,出现了CertificateException等异常,从而不得不做出这种选择。这种做法可能导致的问题是中间人攻击。

  我们在对敏感数据进行传输时应该采用基于SSL/TLS的HTTPS进行传输。由于移动软件大多只和固定的服务器通信,我们可以采用“证书锁定”(certificate pinning)方式在代码更精确地直接验证服务器是否拥有某张特定的证书。实现“证书锁定”的方法有二种:一种是实现X509TrustManager接口,另一种则是使用keystore。具体可以参考Android开发文档中的HttpsURLConnection类的概述。

使用Https进行网络请求

3.1 HTTPS 简介

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

HTTPS 全称 HTTP over SSL/TLS。SSL/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通信

https://www.cnblogs.com/harry335/p/5194917.html

我们以HttpURLConnection为例来做说明:

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

 
  1. public interface X509TrustManager extends TrustManager {

  2. public void checkClientTrusted(X509Certificate[] chain, String authType)

  3. throws CertificateException;

  4.  
  5. public void checkServerTrusted(X509Certificate[] chain, String authType)

  6. throws CertificateException;

  7.  
  8. public X509Certificate[] getAcceptedIssuers();

  9. }

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

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

直接通过TrustManager校验证书:

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

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

 

总结:https和协议

三.Apk被反编译安全问题

 

代码安全主要是指Android apk容易被反编译,从而面临软件破解,内购破解,软件逻辑修改,插入恶意代码,替换广告商ID等风险。我们可以采用以下方法对apk进行保护:

1.1 代码混淆

  代码混淆可以在一定程度上增加apk逆向分析的难度。Android SDK从2.3开始就加入了ProGuard代码混淆功能,开发者只需进行简单的配置就可以实现对代码的混淆。

1.2 Apk签名校验

  每一个软件在发布时都需要开发人员对其进行签名,而签名使用的密钥文件时开发人员所独有的,破解者通常不可能拥有相同的密钥文件,因此可以使用签名校验的方法保护apk。Android SDK中PackageManager类的getPackageInfo()方法就可以进行软件签名检测。

1.3 Dex文件校验

  重编译apk其实就是重编译了classes.dex文件,重编译后,生成的classes.dex文件的hash值就改变了,因此我们可以通过检测安装后classes.dex文件的hash值来判断apk是否被重打包过。

  (1)读取应用安装目录下/data/app/xxx.apk中的classes.dex文件并计算其哈希值,将该值与软件发布时的classes.dex哈希值做比较来判断客户端是否被篡改。

  (2)读取应用安装目录下/data/app/xxx.apk中的META-INF目录下的MANIFEST.MF文件,该文件详细记录了apk包中所有文件的哈希值,因此可以读取该文件获取到classes.dex文件对应的哈希值,将该值与软件发布时的classes.dex哈希值做比较就可以判断客户端是否被篡改。

  为了防止被破解,软件发布时的classes.dex哈希值应该存放在服务器端。

  另外由于逆向c/c++代码要比逆向Java代码困难很多,所以关键代码部位应该使用Native C/C++来编写。

1.4 逆向工具对抗

  对apk进行重打包常用的工具是apktool,apktool对于后缀为PNG的文件,会按照PNG格式进行处理,如果我们将一个非PNG格式文件的文件后缀改为PNG,再使用apktool重打包则会报错。

1.5 调试器检测

  为了防止apk被动态调试,可以检测是否有调试器连接。在Application类中提供了isDebuggerConnected()方法用于检测是否有调试器连接,如果发现有调试器连接,可以直接退出程序。

  以上是使用比较多的几种保护方法,单独使用其中一种效果不大,应该综合运用。

1.6 加壳保护

  使用加壳程序防止apk逆向是一种非常有效的方式,也是一个趋势。Jack_Jia在《Android APK加壳技术方案》一文中详细阐述了Android apk加壳原理以及几种加壳方案的具体实现。我们可以利用这几种方案对apk进行加壳。

  不过这种加壳方式是在Java层实现的,被反编译的风险仍然很大。为了克服这个缺点,今后可以研究采用如下思路来进行保护:

  将核心业务逻辑代码放入加密的.jar或者.apk文件中,在需要调用时使用Native C/C++代码进行解密,同时完成对解密后文件的完整性校验。如果需要更加安全的保护方法,可以考虑对so文件(Native C/C++代码编译得到的文件)进行加壳。Android so加壳主要需要解决两个问题:

  (1)对ELF文件加壳;

  (2)对Android SO的加载、调用机制做特殊处理。

  其实不管是Linux还是Windows,加壳的思路基本是一致的,简单点无非加密+拆解+混淆,复杂点如Stolen Code + VM等。所以确定好一个加壳方案之后,剩下的就是了解elf的文件结构和加载机制,然后自己写一套壳+loader。

  Android上的loader是/system/bin/linker,跟linux上的ld有一些区别。但主要过程还是一致的:map + relocate + init。

  Android so主要充当的角色是通过JNI与java交互,所以主要是作为一个库存在(也有一些so是可执行的),然后被Android runtime加载,并能被java层调用。所以对elf加完壳之后,还要对Android so做一些特殊处理:

  1. Android so被System.LoadLibrary()加载之后,会将so的信息存储在一个全局链表里,所以要保证脱壳后的so能被这个链表访问到。

  2. Android so库函数被java调用有两种方式:一种是通过registerNative注册,另一种是通过javah命名规则命名(参考http://blog.csdn.net/sno_guo/article/details/7688227)。所以一个通用的加壳方案要保证所有的库函数都能被调用,前者好解决,后者需要花点功夫。

 

 

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

 

六、加固方案

 

说了这么多了感觉还是不安全,的确肯定不安全,因为上面的工作你要是还是放在java层去做那么等于没说,永远记住NDK不仅仅是为了性能效率考虑的,也是为了安全考虑使用,很多人看得懂Java代码但是看不懂arm指令,如果在Native层做了这么多防护肯定效果会更好,当然如果你的应用为了更安全那就要考虑加固了,加固是最终方案了至于什么加固厂商那就要自己选择了,但是现在很多大公司不会考虑加固原因有两个:

 

第一、现在没有一个加固厂商跑出来说我们家的加固方案最牛逼,保证0崩溃率,只要有崩溃率那么对于千万级的应用也是很难忍受的,因为每天都有几千甚至几万用户手机的应用在崩溃会疯的。

 

第二、现在加固厂商也就那么几家,用过加固的人或者没用过用腚想想也知道,肯定需要上传签名文件的,因为他加固最后还得重新签名,最主要的是你把没加固前的应用给他,你能保证他和我一样很正直不会偷窥你的应用?不会在你的应用中加点什么他们想要的?所以现在很多公司为了更安全点算了还是不去找加固厂商了。本来还安全点结果弄得不安全了。

 

现在也有很多公司已经在投入人力物力财力自己做加固,就是自己公司开发一套加固方案然后用于自己公司的所有产品,这样肯定是最安全的了,但是这个前提是人力物力财力的投入,如果小公司那肯定是不会考虑的。所以加固本身来说技术肯定是最高端的也是最终的方案的。但是也不是说百分百安全,因为这个世界上还有一个完美的动作叫做脱壳。

 

4.2 混淆

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

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

开启ProGuard:

 
  1. android {

  2. ...

  3. buildTypes {

  4. release {

  5. minifyEnabled true

  6. proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'

  7. }

  8. }

  9. }

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

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

 

关于这种方式,那就是现在很多应用都用的一种方式了,也是安全性最高的一种防护了,他加固主要有三方面:

1、对dex文件进行加密

这样我们用dex2jar工具,或者apktools等工具反编译失败,关于这个dex加密这里,也不做太多的介绍了,之前有一篇文章已经介绍了dex加固的原理了:Android中apk加固原理解析   

破解之道:

但是可惜的是,这种方式也沦陷了,因为我们知道,不管你dex怎么加密,最后都是需要用DVM加载dex文件到内存中的,我们知道Android中所有关于DVM的函数都是在libdvm.so文件中的,而且这个文件是存在设备的/system/lib目录中的,加载dex有一个重要的函数:

int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
第一个参数就是dex内存起始地址

第二个参数就是dex大小。所以在这个函数下断点可以直接dump出明文dex

所以我们使用IDA调试程序,找到模块libdvm.so的内存地址,找到其中的函数dvmdexfileopenpartialPKviPP6DvmDex,在这个函数下断点即可dump处dex文件了。

关于这个案例,后续还会写一篇文章来介绍如何破解现在加固dex的应用。

2、对so文件进行加密

参考博客:

https://blog.csdn.net/jiangwei0910410003/article/details/49967375

现在很多应用把中号的功能都放到了native层中,那么如果我们队so文件进行加密的话,那么IDA工具就无法打开so文件,从而做到安全保护,关于Android中so加密可以参考这篇文章:Android中对so加固原理解析  

破解之道:

但是可惜的是,这种方式也被沦陷了,看完这篇文章之后,我们知道加固so的有一个特点就是你必须在so在被调用的时候需要进行解密,不然会影响正常的native层调用,那么这个时机很重要,一般都是在so文件的入口处,也就是用:

 __attribute__((constructor)) 这个属性来标注一个解密函数,这样就能保证解密函数执行的时机比任何一个函数时机都早,或者可以理解为是一个类的构造函数的执行时机即可,但是一般有这种属性的函数都在so的.inin_array段中的:

那么现在的问题就变成了,如果我知道了.init_array端的位置,然后在查看这个段中的arm指令代码,就可以得到解密函数了,然后在解读这个解密函数的逻辑即可。那么最后就要看这个加密函数的难度程度怎么样了。

关于这个案例,后续还会写一篇文章来介绍如何破解现在加固so的应用。

3、加固资源文件和AndroidManifest.xml文件

这个加固一般是应对与现在最流行的反编译工具apktool了,他是开源的,比如下面的这个应用:

所以看到了,这种加固就是利用apktool工具的漏洞来进行加固的,不过这个apktool工具也是实时在更新的,也是为了解决现在的apk这种资源文件的加固导致反编译失败的问题。

破解之道:

所以对于这种反编译失败的问题,我们应该自己编译apktool的源码,找到指定的保存位置,然后修改异常即可,不过这个可不是一个简单的工作,是需要耐心和经验的。

 

 

动态库被第三方APP非法使用

加壳

加壳的原理是通过加密原应用的安装包中的dex文件,其主要操作方式大致如下:

  1. 准备要进行加壳的原应用安装包(以下简称原apk)、用于做壳的安装包(以下简称壳apk);
  2. 对原Apk进行拆解获取各个部分,并将dex文件进行算法加密(以下简称加密原dex);
  3. 将加密原dex和壳Apk中的dex进行组合,合并成为新的dex文件;
  4. 利用特制的打包工具合并生成加密后的apk;

这种通过隐藏dex文件的方式加壳方式,最终是利用ClassLoader在内存中解密并进行动态加载运行。而如果是修改dex文件的加壳方式,其主要是抽取DexCode中的字节码指令后用零去填充,或者修改方法属性等操作,其修复时机则是运行时在内存中做相应的修正工作。

通过加壳得到的安装包如果不进行脱壳操作,逆向人员就无法拿到真正的dex文件,也就无从分析。这里可以看看使用360加固的一个应用的结构在没脱壳前的安装包结构

只有寥寥几个类,而正真的安装包中的dex文件则被藏起来了,这进一步加大了逆向的难度。关于加壳,市面上已经有很多成熟企业加固方案可以使用,如梆梆安全、爱加密、360加固保等,如果不是专门研究这块的开发者去自行开发一套加壳方案,显然不太现实。

加壳也只是提高被逆向的门槛,对于功力不够的逆向开发者而言,只能就此作罢,而对于逆向老鸟来说,脱壳同样这是外包时间问题罢了。此外,对应用加壳还要留意平台兼容性问题,如此前某著名加固产品就出现过在ART虚拟机不兼容问题,以及将会影响项目使用某些热修复技术。

 

签名校验

Android黑产里面,有一个叫做二次打包,也称为重打包。即通过反编译正版应用后,可以获得smali源码,往其中注入代码或者修改相应业务逻辑后,再利用新的签名进行重新打包,并发布到应用市场去,很多无良开发者就是通过这种方式去破解一些付费应用或者往其中注入广告代码来获利。简单梳理一下重打包的基本流程:

  1. 对正版应用用apktool类逆向工具进行解包;
  2. 在某处地方注入smali代码;
  3. 利用IDE生成签名文件,再通过jarsigner进行签名;
  4. 上传应用市场;

为了与二次打包做对抗,可以在应用内的关键功能入口增加校验签名的检测,如果发现应用签名非正版,则强制关闭应用或者限制用户使用。加签名校验代码时,可以考虑:

  1. 在JNI层中加校验代码,相比在Java层的代码,JNI层的逆向难度更大;
  2. 如果要在Java层加校验代码,不要在一个地方暴露一段长串字符串,对于逆向工程师是来说,这是非常明显的提示。可以考虑将字符串打散存放在各处,这样会增加破解分析的难度;

当然,不要以为放在JNI就高枕无忧,对于JNI层,同样可以进行代码注入,来暴力破解你签名校验的逻辑,只不过相比Java层的,JNI层所需成本更高,这样也就能拦截掉一部分逆向人员的歪主意。

四. 组件安全

  android应用内部的Activity、Service、Broadcast Receiver等组件是通过Intent通信的,组件间需要通信就需要在Androidmanifest.xml文件中配置,不恰当的组件配置、组件在被调用时未做验证、在调用其他组件时未做验证都会带来风险。

  可能产生的风险包括:

  (1)恶意调用

  (2)恶意接受数据

  (3)仿冒应用,例如(恶意钓鱼,启动登录界面)

  (4)恶意发送广播、启动应用服务。

  (5)调用组件,接受组件返回的数据

  (6)拦截有序广播

  比如:调用暴露的组件发短信、微博等。

  解决办法: 

   (1)最小化组件暴露

   不参与跨应用调用的组件添加android:exported="false"属性,这个属性说明它是私有的,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务。

      (2)设置组件访问权限

   对参与跨应用调用的组件或者公开的广播、服务设置权限。只有具有该权限的组件才能调用这个组件。

    (3)暴露组件的代码检查

   Android 提供各种API来在运行时检查、执行、授予和撤销权限。这些API 是 android.content.Context 类的一部分,这个类提供有关应用程序环境的全局信息。

常见的几种防范措施

 

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:

     
    1. "http://schemas.android.com/apk/res/android"

    2. ...

    3. android:sharedUserId="xxx.xxx.xxx">

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

 
  1. ".xxxActivity"

  2. android:label="自定义permission"

  3. android:permission="com.xxx.permission" >

  4.  
  5. "android.xxx.action" />

  6. "android.intent.category.DEFAULT" />

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

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

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

组件安全==避免WebView使用漏洞

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

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

原因

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

 
  1. webView.addJavascriptInterface(new JSObject(), "myObj");

  2. // 参数1:Android的本地对象

  3. // 参数2:JS的对象

  4. // 通过对象映射将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_对象
  • 该接口可能被利用,实现远程任意代码。

解决办法

 
  1. webview.removeJavascriptInterface("searchBoxJavaBridge_");

  2. webview.removeJavascriptInterface("accessibility");

  3. 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及以后的版本中默认已被禁止。

解决办法

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

 
  1. setAllowFileAccess(false);

  2. setAllowFileAccessFromFileURLs(false);

  3. setAllowUniversalAccessFromFileURLs(false);

2.4 WebView密码明文存储漏洞

原因

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

解决办法

webView.setSavePassword(false)

组件安全:

1.密码软键盘

2.手势密码

3.短信重放攻击 

数据传输安全

1.http

2.证书有效性验证 

数据存储安全例子:

1.登入的数据保存在哪里

2.密钥的存放位置

3.录音文件,图片文件存储

安卓系统本身的安全

1.Root

检查设备是否被root。你可以通过允许你使用终端的程序来检查设备是否已经被root。来看一个例子:

2.权限控制

3.签名

 

 

Android应用安全解决方案 

  Android应用会遇到各种各样的安全性问题,如何从宏观上了解各种安全隐患,积极采取适当的防御措施便变得尤为重要。那么,Android应用面临哪些安全问题呢?

  • 进程沙箱隔离机制

进程沙箱隔离机制,使得Android应用程序在安装时被赋予独特的用户标识(UID),并永久保持。应用程序及其运行的Dalvik虚拟机运行在独立的Linux进程空间,与其它应用程序完全隔离。

  在特殊情况下,进程间还可以存在相互信任关系。如源自同一开发者或同一开发机构的应用程序,通过Android提供的共享UID(Shared UserId)机制,使得具备信任关系的应用程序可以运行在同一进程空间。

  应用程序签名机制,规定APK文件必须被开发者进行数字签名,以便标识应用程序作者和在应用程序之间的信任关系。在安装应用程序APK时,系统安装程序首先检查APK是否被签名,有签名才能安装。当应用程序升级时,需要检查新版应用的数字签名与已安装的应用程序的签名是否相同,否则,会被当做一个新的应用程序。Android开发者有可能把安装包命名为相同的名字,通过不同的签名可以把他们区分开来,也保证签名不同的包不被替换,同时防止恶意软件替换安装的应用。

  权限声明机制,要想获得在对象上进行操作,就需要把权限和此对象的操作进行绑定。不同级别要求应用程序行使权限的认证方式也不一样,Normal级申请就可以使用,Dangerous级需要安装时由用户确认,Signature和Signatureorsystem级则必须是系统用户才可用。

  访问控制机制,确保系统文件和用户数据不受非法访问。


 

  病毒不用多说了,都是一些恶意软件。关键信息泄露,可能有些开发者并不十分留意。虽然Java代码可以做混淆,但是Android的几大组件的创建方式是依赖注入的方式,因此不能被混淆。而且目前常用的一些反编译工具比如apktool等能够毫不费劲地还原Java里的明文信息,native里的库信息也可以通过objdump或IDA获取。因此一旦Java或native代码里存在明文敏感信息,基本上就是毫无安全而言的。重打包即通过反编译后重新加入恶意的代码逻辑,重新打包一个APK文件。进程被劫持一般通过进程注入或者调试进程的方式来hook进程,改变程序运行的逻辑和顺序,从而获取程序运行的内存信息。hook需要获取root权限或者跟被hook进程相同的权限。如果手机没被root,被劫持的可能性还是较小。数据在传输过程遭劫持,一般来说是由于数据明文传输或没使用HTTPS。Webview漏洞一般由于JS注入。

  现实中,出现的问题可能比上面提及的还要多。总的来说,应该从以下几个方面来应对Android开发的常见安全问题:

  • 应用权限控制。通过控制
    应用程序的权限防止恶意应用对系统造成破坏,采取的措施包括合理使用系统内置权限和应用程序自定义权限。
  • 应用程序签名。采用数字签名为应用程序签名。
  • 应用加固。应用加固包括病毒扫描、防注入、防调试、防篡改四个模块,目前行业内已经出现了很多的应用加固解决方案,如360应用加固、腾讯云应用加固、百度应用加固等等。 
  • 静态代码分析。通过静态代码分析工具lint监测安全隐患,对代码进行优化。
  • 防火墙。必要时为Android设备安装防火墙,以防止远程网络攻击。
  • 数据存储加密。采用加密的方式保护应用程序敏感数据,如利用SQLCipher加密SQLite数据库。
  • 应用程序组件开发的安全要点。Activity, Service, Content Provider, Broadcast Receiver等组件在代码层面应采取的安全措施。它们每一个都可以通过隐式的Intent方式打开,所以这些组件只要不是对外公开的必须在AndroidManifest里面注明exported为false,禁止其它程序访问我们的组件。对于要和外部交互的组件,应当添加访问权限的控制,还需要要对传递的数据进行安全的校验。

应用是怎么被攻击的,攻击的方式

App的移动安全主要包括下面几种:

 

  1. 密钥破解,导致本地加密数据被盗取
  2. 通信密钥破解,导致接口数据被盗取
  3. 伪造接口数据上报
  4. 接口签名被破解,导致接口可以被重放攻击
  5. 、防止应用被抓包三、https防抓包机制,拒绝被hook,防止应用被Hook操作
  6.  .反调试异常检测

那么归结起来,实际上就是这样几种模式:

  1. 代码反编译
  2. so破解
  3. 中间人攻击

 

项目案例:

现在项目里面有一个需求,本项目里面下载的视频和文档都不允许通过其他的播放器播放,在培训机构里面这样的需求很多。防止有人交一份钱,把所有的课件就拷给了别人。这样的事情培训机构肯定是不愿意的。现在我项目里面也出了这么个需求。下面介绍一下我的实现。

思路:

首先下载文件,这个就不说了,java代码写个下载管理器。

下载完成后存储文件的时候不是直接存储,要加密存储,加密方法是将文件的每个字节与这个字节在流中的下标做异或运算。

在我们项目里面播放的时候要解密,方法也是将文件的每个字节与这个字节在流中的下标做异或运算。两次异或得到的就是没有加密的值。


 

 

 

安全问题

https://blog.csdn.net/jiangwei0910410003?t=1

https://blog.csdn.net/u013409903/article/details/76686155

https://www.cnblogs.com/GOODHACKER/P/3864680.HTML

 参考博客:

https://blog.csdn.net/guolin_blog/article/details/50451259(郭霖)

你可能感兴趣的:(面试)