如何保障Android应用安全

在本文中,你将学习到一些提高Android应用安全级别的最佳实践。

在Android中安全是你无法保证的事情。作为一个开发者,你无法知道你的应用是否足够安全。每一个系统都会被破解但是你可以让攻击者的日子更加难熬。

网络

现在几乎所有的应用都会通过网络和服务器交换用户数据、令牌。

你应该考虑用户网络链接的安全以及怎么保护他们的数据不被盗取。

保证你的网络链接更加安全的第一步就是使用HTTPS,当然这还不够。

最流行的网络攻击之一就是中间人攻击(MITM)。它可以是被动的也可以是主动的。

为了让你的应用免遭被动MITM攻击,你只需要使用迪菲-赫尔曼密钥交换算法视频介绍)就可以。

主动MITM攻击会强大一些。使用SSL锁定可以使你的数据免遭盗取。

有许多工具支持HTTPS和SSL锁定。RetrofitOkHttp就是其中的两个,在UPTech我们几乎处处使用它们。

Retrofit使用起来非常简单,它还支持RxJava,也不需要花很长的时间来配置。

在OkHttp的帮助下,你还可以添加你自己信任的SSL证书。

“OkHttp默认是信任服务平台的证书授权的。证书认证可以提高安全性,但是它限制了服务端团队升级其TLS证书的能力。在没有得到服务端TLS管理员许可的情况下不要使用证书认证!”----Jesse Wilson, Square Inc

关于SSL认证以及怎样安装你可以参考这篇文章

意图(Intent)

你知道应用之间可以通过Intent交互。Intent的类型有两种:显式的和隐式的。

显式意图

优点:不会被拦截。
缺点:只能在应用内部使用。

隐式意图

优点:可以在Android系统的任何地方使用。
缺点:很容易被拦截。

攻击者只需要简单的定义一个相同的intent-filter就可以拦截你的intent并获取所有数据。

为了避免这种情况,你应该尽可能的使用显式意图。例如,你可以指定包名。

Intent intent = new Intent(Intent.ACTION_SEND);
intent.setPackage("com.test.package");
sendBroadcast(intent);

不同外部模块交互的Broadcast Receiver或者Services不要暴露。

<service
    android:name=".service.SomeService"
    android:enabled="true"
    android:exported="false">

    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</service>

结论

在你的应用中尽可能的使用显式意图。明确的指定你想发送数据的接收者。

数据存储

内存缓存

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

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

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

比起在外部存储器中打开一个文件就可以获取到密码,攻击者要dump内存则就难得多。Dump内存需要ROOT权限。
Android官方文档中关于内存缓存的说明可以戳这里(以缓存图片为例)

加解密(Cipher)

此外,你可以在将数据存入缓存、数据库或者SharedPreferences之前将其加密。这里我推荐使用Cipher。

Cipher是一个内建的数据加密/解密工具。有关Cipher参数的更多信息请戳这里

举一个使用Cipher的例子:

private static byte[] encrypt(byte[] key, byte[] input) throws Exception {
    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(input);
    return encrypted;
}

private static byte[] decrypt(byte[] key, byte[] encrypted) throws Exception {
    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    byte[] decrypted = cipher.doFinal(encrypted);
    return decrypted;
}

你应该了解某些加密算法(比如 AES)需要固定的密钥长度,如128,192或者256bit。
更多Clipher所支持算法的详细信息请戳这里

Shared Preferences

和其他例子一样,需要加密所有的数据,不要将原始文本直接存储在Shared Preferences中。所有你想存放到Shared Preferences的数据都应该以“MODE_PRIVATE”属性写入,Android默认就是这个属性。

有了“MODE_PRIVATE”属性就能保证只有你的应用能够读写数据。当然如果没有ROOT权限谁也无法获取你的数据。

还有另外一个工具即SecureSharedPreferences,你可以在这里了解更多的信息。

这个工具使用起来也很简单,它是在原生的Shared Preferences基础添加了很多加密算法。你不需要对它添加的新功能很了解,按照默认的Shared Preferences方式使用即可。

SharedPreferences prefs = new SecurePreferences(context, "userpassword", "my_user_prefs.xml");

这是它加密以后的样子:

<map>
    <string name="TuwbBU0IrAyL9znGBJ87uEi7pW0FwYwX8SZiiKnD2VZ7">
   pD2UhS2K2MNjWm8KzpFrag==:MWm7NgaEhvaxAvA9wASUl0HUHCVBWkn3c2T1WoSAE/g=rroijgeWEGRDFSS/hg
    </string>
    <string name="8lqCQqn73Uo84Rj">k73tlfVNYsPshll19ztma7U>
</map>

密钥

下一个问题就是将你加密算法的密钥存放在哪里。这是一个没有最佳答案的通用问题。不管你将密钥存放在哪里,攻击者总是能够找到它。唯一能够阻止攻击者破解应用的就是加密算法的复杂性和寻找密钥所需要的时间。

你可以使用KeyStore,但是只有在Android API 18以后的版本才支持。另外一个问题还是ROOT,获取到ROOT权限的攻击者可以获取到任何数据。

或者使用Java Native Interface(JNI)。反编译C/C++编译出来的代码要困难的多。Jadexdex2jar这种面向Java语言的反编译器这时就无法工作了。

还有一个可能的办法就是使用隐写术算法,然后将密钥藏在任何你想藏的地方。例如:你可以将图片中的某些bit替换为你的密钥的bit。

上面描述的所有方法都可以为你的应用的“健康”增加时间。采取一些措施总比什么都不做要好。

结论

关于怎样隐藏或者加密密钥或者数据的算法数以万计。即使最好的、最变态的算法也无法保护你的数据。

最好的办法是不存储任何数据,但是有时是不可能的。

其他建议

关于增加应用安全性还有这些建议:

模拟器

检查你的应用是否正在模拟器上运行。你可以使用android.os包中的Build类来检查。它可以提供很多运行当前应用的设备的信息。如果有兴趣可以戳这里了解更多信息。

Debug

检查当前设备是否有调试器连接。最简单的解决办法是:

Debug.isDebuggerConnected()

Root

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

private static boolean isRooted() {
    return findBinary("su");
}

public static boolean findBinary(String binaryName) {
    boolean found = false;
    if (!found) {
        String[] places = {"/sbin/", "/system/bin/",
                "/system/xbin/", "/data/local/xbin/",
                "/data/local/bin/", "/system/sd/xbin/",
                "/system/bin/failsafe/", "/data/local/"};
        for (String where : places) {
            if (new File(where + binaryName).exists()) {
                found = true;
                break;
            }
        }
    }
    return found;
}

这段代码摘自RootTools这个库。

su命令常常用于将当前用户的权限修改为root用户。戳这里可以了解更多关于su的信息。

一旦这个检测到设备已经被root,通知服务器,暂停连接,中断服务。但是不要做的太过,以至损害用户体验,造成用户流失。

字符串

使用字符数组而不是使用字符串,或者使用 new 操作符来创建一个字符串,这样新创建的字符串是在堆(heap)里而不是在字符串池(string pool)里。

String a = new String("a");
a = new String("a");

在上面的例子中会创建两个对象。在第二行代码被执行后,垃圾回收器(GC)就会回收第一个对象。

String a = "a";
String b = "a";

在这个例子中,只会创建一个对象。因为使用了字符串,对象会保存在字符串池(string pool)中,垃圾回收器暂时还不会回收它。

混淆

使用ProGuardDexGuard或者DexProtector等混淆器。

ProGuard是Android Studio默认的代码精简工具,它也可以混淆你的代码。在创建新的Android Studio工程时会自动生成一个ProGuard配置文件。在你的工程中是以proguard-rules.pro命名的。

在这个文件中你可以指定编译APK时需要执行的规则。你也可以在这里发现流行库所使用的一些规则。

开启ProGuard你需要这样做:

android {
   buildTypes {
      dev {
          minifyEnabled true  // enables ProGuard
          proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
      }
}

使用混淆你可以让攻击者困惑从而花更多的时间来破解你的应用。

有许多公司提供混淆工具。这里有一份最流行的混淆器的清单。其中最好的那个相当的贵。它们能把你的应用的代码逻辑转换得晦涩难懂。不仅如此,花更多的钱你还可以开启修改root/debug/emulator/MITM/Code等选项的功能。

一些有用的链接

Android 关于安全的文档

移动应用排名前十的风险

安全测试工具

总结

我尽力在这篇8分钟长的文章里提供大量的信息。希望你能有所收获。并不是所有的方式你都需要严格遵循,但是你可以尝试一些新的东西(或者不是新的 :))。

关于Android安全再啰嗦两句。在攻击者获得ROOT权限之前,每一条建议都很有用。一旦ROOT权限击溃你所有的保护,再无回天之力。希望未来发布的Android系统会更加安全。

如果你有任何问题、建议或者你知道某一领域我可以研究的都请告诉我。谢谢!

文本译自How To Make Your Android Application Secured

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,513评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,312评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,124评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,529评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,937评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,913评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,084评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,816评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,593评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,788评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,267评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,601评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,265评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,158评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,953评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,066评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,852评论 2 277

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,591评论 25 707
  • 本文主要介绍移动端的加解密算法的分类、其优缺点特性及应用,帮助读者由浅入深地了解和选择加解密算法。文中会包含算法的...
    苹果粉阅读 11,306评论 5 29
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,107评论 18 139
  • 我们本身就在一个矛盾的世界生活,只要有盾,就一定会有刺穿这个盾牌的矛!跟大家分析一个小案例。我们以前公司有位同事,...
    小清哥阅读 526评论 0 0
  • 你听过人工影响天气作业吗? 你知道‘’天有不测风云‘’这句话现在开始遇到真正的挑战了吗? 我现在推广的项目“天气随...
    世说新解阅读 122评论 0 0