Android 安全防护

707 阅读9分钟

App为什么要做防护?

Android的开发语言是java,所以有编译好的java语言会被反编译得到源码。而各种各样的工具使得反编译更容易。获取到项目的源码后就可以做一些特别的操作,导致公司损失。

防护手段

为了应对反编译破解,自然而然的出现了一些应对手段,基本总结如下

  • 资源混淆
  • 代码混淆
  • 签名校验
  • 组件安全
  • Webview 的代码执行漏洞
  • 加固
  • 编码安全
  • 动态加载
  • hook
  • 数据存储安全
  • 数据传输安全
  • 内存数据安全

资源混淆

在常用的反编译工具,很容易反编译apk包,获取资源文件,反编译很容易获取到anim、drawable、layout、menu、values等文件夹中的信息,可以通过修改这些文件夹下的资源文件,并通过apktool进行回编译(apktool b 命令)就能创建一个经过修改过的APK应用 这里有一种技术方案AndResGuard 这既是瘦身也可以混淆

代码混淆

Android 开发中代码混序是很常用的防护手段,代码混淆是包含了代码压缩、优化、混淆等一系列行为的过程。大致包括以下步骤:

  • 压缩。移除无效的类、类成员、方法、属性等;
  • 优化。分析和优化方法的二进制代码;根据proguard-android-optimize.txt中的配置
  • 混淆。把类名、属性名、方法名替换为乱七八糟的名称;
  • 预校验。添加预校验信息。android 不需要 常用的混淆语法比较对且比较简单,感兴趣的可以自己查阅

获取混淆文件

  1. 开启混淆
  2. 将混淆文件导入到 proguard-rules.pro 同一目录下
  3. 编辑proguard-rules.pro,添加如下内容 比较容易实现,使用已有的混淆文件 github.com/WrBug/Frenz… 或者自定义

三方加固包,付费的比较有用

签名校验

Android系统使用JAR包的签名机制对APK进行完整性保护,确保APK在不安全的网络传输时的完整性得到保护。但Android系统没有对数字签名的颁发者进行管理,任何人都可以生成数字签名,并使用该签名对APK包进行重新签名。如果App本身不对自身的签名来源进行有效的完整性检查,攻击者可以篡改应用(插入恶意代码、木马、后门、广告等),重新签名并且二次发布,导致应用程序完整性被破坏

www.jianshu.com/p/5c4a4b1e8…

组件安全

Activity防护

Activity调度机制

android在不同的应用程序之间的切换,基本上是无缝。他们切换的只是一个activity,让切换的到前台显示,另一个应用则被覆盖到后台,不可见。而Activity的调度是交由Android系统中的AMS管理的(ActivityManagerService)。activity的启动和停止都是由AMS控制的。Activity会存入栈中

Activity劫持

就是正常的activity被攻击者替换上一个假冒的activity,但是用户并不知道这是一个假冒的activity,所以用户在输入相关信息后,就直接被攻击者获取到了。通常像登录页面,支付页面,或者是其他数据较为敏感的页面都是很容易被攻击。例如写一个程序,启动一个后台服务不断扫描进行,然后伪造一个页面,回去用户的信息。 解决:

  • 当我们的应用程序退到后台的时候,弹出提示信息提示用户,程序已经退出到后台。
  • 设置 Android:exported 属性,不需要被外部程序调用的组件应该添加 android:exported="false" 属性。

Service劫持,Service拒绝服务

Service劫持

在Android系统中,如果存在多个Service接收同一个action的时候,首先看他们的priority值,priority值高,就先启动哪个。如果priority值一样,就看Service所属应用程序的安装顺序,启动先安装应用的Service。

  • 当创建Intent的时候,显示的指定要启动的Service的包名和类名,不使用action方式启动。
Service拒绝服务

Android系统中,主要是通过intent来携带数据进行跳转的。但是系统在内部对intent中数据的并没有进行一些处理,比如数据为空,数据有问题等等,一旦攻击者对intent中的数据进行改动,就导致程序崩溃。

  • 处理getIntent()的intent附带的数据;
  • 空指针异常;
  • 类型转换异常;
  • 数组越界访问异常;
  • 类未定义异常;
  • 其他异常;ent()的intent附带空数据、异常或畸形数据;

Webview 的代码执行漏洞

漏洞主要有三种

  • 任意代码执行漏洞
  • 密码明文存储漏洞
  • 域控制不严格漏洞
WebView 任意代码执行漏洞
  • WebView 中 addJavascriptInterface() 接口

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

漏洞产生原因是:当JS拿到Android这个对象后,就可以调用这个Android对象中所有的方法,包括系统类(java.lang.Runtime 类),从而进行任意代码执行。如

  • Android中的对象有一公共的方法:getClass() ;
  • 该方法可以获取到当前类 类型Class
  • 该类有一关键的方法: Class.forName;
  • 该方法可以加载一个类(可加载 java.lang.Runtime 类)
  • 而该类是可以执行本地命令的

解决方案:
Android 4.2版本之后:

Google 在Android 4.2 版本中规定对被调用的函数以 @JavascriptInterface进行注解从而避免漏洞攻击

Android 4.2版本之前:
采用拦截prompt()进行漏洞修复
原理
每次当 WebView 加载页面前加载一段本地的 JS 代码,原理是:

  • 让JS调用一Javascript方法:该方法是通过调用prompt()把JS中的信息(含特定标识,方法名称等)传递到Android端;
  • 在Android的onJsPrompt()中 ,解析传递过来的信息,再通过反射机制调用Java对象的方法,这样实现安全的JS调用Android代码。

细节1:加载上述JS代码的时机

onLoadResource();
doUpdateVisitedHistory();
onPageStarted();
onPageFinished();
onReceivedTitle();
onProgressChanged();

细节2:需要过滤掉 Object 类的方法

getClass()
hashCode()
notify()
notifyAl()
equals()
toString()
wait()

密码明文存储漏洞

WebView默认开启密码保存功能,开启后,在用户输入密码时,会弹出提示框:询问用户是否保存密码;密码会被明文保到 /data/data/com.package.name/databases/webview.db 中,这样就有被盗取密码的危险
解决方案:

WebSettings.setSavePassword(false) 

域控制不严格漏洞

1. setAllowFileAccess()
// 设置是否允许 WebView 使用 File 协议
webView.getSettings().setAllowFileAccess(true);     
// 默认设置为true,即允许在 File 域下执行任意 JavaScript 代码

使用 file 域加载的 js代码能够使用进行同源策略跨域访问,从而导致隐私信息泄露

  1. 同源策略跨域访问:对私有目录文件进行访问
  2. 针对 IM 类产品,泄露的是聊天信息、联系人等等
  3. 针对浏览器类软件,泄露的是cookie 信息泄露。 如果不允许使用 file 协议,则不会存在上述的威胁;但同时也限制了 WebView 的功能,使其不能加载本地的 html 文件

解决方案:

  1. 对于不需要使用 file 协议的应用,禁用 file 协议;
  2. 对于需要使用 file 协议的应用,禁止 file 协议加载 JavaScript。
setAllowFileAccess(true); 

// 禁止 file 协议加载 JavaScript
if (url.startsWith("file://") {
    setJavaScriptEnabled(false);
} else {
    setJavaScriptEnabled(true);
}
2. setAllowFileAccessFromFileURLs()
// 设置是否允许通过 file url 加载的 Js代码读取其他的本地文件
webView.getSettings().setAllowFileAccessFromFileURLs(true);
// 在Android 4.1前默认允许
// 在Android 4.1后默认禁止

解决方案: 设置setAllowFileAccessFromFileURLs(false);

3. setAllowUniversalAccessFromFileURLs()
// 设置是否允许通过 file url 加载的 Javascript 可以访问其他的源(包括http、https等源)
webView.getSettings().setAllowUniversalAccessFromFileURLs(true);

// 在Android 4.1前默认允许(setAllowFileAccessFromFileURLs()不起作用)
// 在Android 4.1后默认禁止

解决方案 设置setAllowUniversalAccessFromFileURLs(false);

4. setJavaScriptEnabled()
// 设置是否允许 WebView 使用 JavaScript(默认是不允许)
webView.getSettings().setJavaScriptEnabled(true);  

// 但很多应用(包括移动浏览器)为了让 WebView 执行 http 协议中的 JavaScript,都会主动设置为true,不区别对待是非常危险的。

即使把setAllowFileAccessFromFileURLs()和setAllowUniversalAccessFromFileURLs()都设置为 false,通过 file URL 加载的 javascript 仍然有方法访问其他的本地文件:符号链接跨源攻击

前提是允许 file URL 执行 javascript,即webView.getSettings().setJavaScriptEnabled(true);

通过 javascript 的延时执行和将当前文件替换成指向其它文件的软链接就可以读取到被符号链接所指的文件。具体攻击步骤:

  1. 把恶意的 js 代码输出到攻击应用的目录下,随机命名为 xx.html,修改该目录的权限;
  2. 修改后休眠 1s,让文件操作完成;
  3. 完成后通过系统的 Chrome 应用去打开该 xx.html 文件
  4. 等待 4s 让 Chrome 加载完成该 html,最后将该 html 删除,并且使用 ln -s 命令为 Chrome 的 Cookie 文件创建软连接

最终解决方案
对于不需要使用 file 协议的应用,禁用 file 协议;

// 禁用 file 协议;
setAllowFileAccess(false); 
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);

对于需要使用 file 协议的应用,禁止 file 协议加载 JavaScript。

// 需要使用 file 协议
setAllowFileAccess(true); 
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);

// 禁止 file 协议加载 JavaScript
if (url.startsWith("file://") {
    setJavaScriptEnabled(false);
} else {
    setJavaScriptEnabled(true);
}

加固

付费的三方加固

编码安全

  • 重要的字符不要硬编码在 Java 层,因为 String 内容是不会被混淆的,可以放在 JNI 中,JNI 函数也可以再加上一层保护措施。
  • 限制对变量的访问,让每个量的方法都成为 final,尽量使你的类不要被克隆,如果一旦允许被克隆,它可能会绕过这个类轻易地复制类的属性。
  • Android 中很多会使用到序列化,如果自己觉得这个类是有风险的,或者说安全性是很高的,尽量不要让它序列化。

hook防护

www.jianshu.com/p/4ecc288a0…

数据存储安全

  1. SharedPreferences 存储加密解密方式:对 key 和 value 同时加密,存储类型都为 2. String 类型,数据读取时根据需要进行类型转换。
  2. File 文件存储加密解密方式:对数据流进行加密解密。
  3. SQLite 数据库存储加密解密方式:基于 Sqlcipher 进行实现。

数据传输安全

使用 CA 机构颁发证书的方式可行,但是如果与实际情况相结合来看的话,时间和成本太高,所以目前很少有用此办法来做。由于手机应用服务器其实是固定的,所以证书也是固定的,可以使用"证书或公钥锁定"的办法来防护证书有效性未作验证的问题。

内存数据安全

对于重要的内存数据,比如密码,最好不要长时间驻留在内存中,用完立即销毁。如果非要长时间驻留,可以考虑加密加盐方式。