查看原文
其他

你开发的app可能正饱受风险

brucevanfdm 鸿洋 2019-04-05

本文作者


作者:brucevanfdm

链接:

https://www.jianshu.com/p/7f2202c18012

本文由作者授权发布。


1前言


近期facebook的隐私泄露事件再度唤起了公众对隐私的关注,在这个越来越重视个人隐私的时代里,安全早已是一个无法绕开的话题。


作为一个开发人员,必须具备安全意识,掌握基础的安全知识,为打造更加安全的应用做出努力。本文浅谈Android客户端的安全问题,涉及组件、WebView、存储、传输、日志、混淆、应用加固等安全漏洞及防护策略,运用更加合理的配置与防护措施来提高应用的安全级别。


2组件安全


规范安卓标准组件(Activity、Service、Receiver、Provider)的访问权限。


这里提到的组件访问权限主要是指跨应用的进程间通信(IPC,Inter-Process Communication),开发者可以限定应用内的组件是否允许被其他应用调起,或接收来自外部应用的数据,或者访问我们的数据。并且必须对来自外部应用的数据做校验处理,避免来自外部攻击。如恶意调用组件、广播数据攻击、恶意访问数据等等。


1. 设置权限开放属性:android:exported=["true" | "false"]


exported属性为四大组件共有属性,其中含义大同小异。默认值由其包含 <intent-filter> 与否决定。若未包含<intent-filter>,默认为“false”,若存在至少一个<intent-filter>,则默认值为“true”。


在Activity中:


表示是否允许外部应用组件启动。若为“false”,则 Activity 只能由同一应用或同一用户 ID 的不同应用启动。


在Service中:


表示是否允许外部应用组件调用服务或与其进行交互。若为“false”,则 Activity 只能由同一应用或同一用户 ID 的不同应用启动。


在Receiver中:


表示是否可以接收来自其应用程序之外的消息,如来自系统或或其他应用的广播。若为“ false”,则广播接收器只能接收具有相同用户ID的相同应用程序或应用程序的组件发送的消息。


在Provider中:


表示是否允许其他应用程序访问内容提供器。若为“false”,则具有与Provider相同的用户ID(UID)的应用程序才能访问它。如果需要给其他应用程序提供内容,则应当限定读写权限。


2. 配置自定义权限


在AndroidManifest.xml中定义一个名称为com.fedming.demo.ACCESS的权限:


<permission android:description="string resource"
           android:icon="drawable resource"
           android:label="string resource"
           android:name="com.fedming.demo.ACCESS"
           android:permissionGroup="string resource"
           android:protectionLevel=["normal" | "dangerous" | "signature" | ...] />


android:protectionLevel="signature"表示签名保护级别,“签名”级别权限的内容对用户完全透明开放,而且只有由执行权限检查的应用的开发者签名的应用才可访问这些内容。


应用到四大组件或者Application中android:permission="com.fedming.demo.ACCESS",即可实现对非同一签名应用的限制性访问。一般用于一些内部应用之间共享的私有组件。


使用更加安全高效的LocalBroadcastManager


区别基于Binder实现的BroadcastReceiver,LocalBroadcastManager 是基于Handler实现的,拥有更高的效率与安全性。安全性主要体现在数据仅限于应用内部传输,避免广播被拦截、伪造、篡改的风险。简单了解下用法:


1.自定义BroadcastReceiver

public class MyReceiver extends BroadcastReceiver {
   @Override
   public void onReceive(Context context, Intent intent) {
//Do SomeThing Here
   }
}


2. 注册Receiver


MyReceiver myReceiver = new MyReceiver();
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
IntentFilter filter = new IntentFilter();
filter.addAction("MY_ACTION");
localBroadcastManager.registerReceiver(myReceiver, filter);


3. 发送本地广播

Bundle bundle = new Bundle();
bundle.putParcelable("DATA", content);
Intent intent = new Intent();
intent.setAction("MY_ACTION");
intent.putExtras(bundle);


4. 在Activity销毁时取消注册

@Override  
protected void onDestroy() {  
  super.onDestroy();  
  localBroadcastManager.unregisterReceiver(myReceiver);    
}  


Application相关属性配置


1. debugable属性 android:debuggable=["true" | "false"]

很多人说要在发布的时候手动设置该值为false,其实根据官方文档说明,默认值就是false。


2. allowBackup属性 android:allowBackup=["true" | "false"]

设置是否支持备份,默认值为true,应当慎重支持该属性,避免应用内数据通过备份造成的泄漏问题。


3WebView安全


1. 谨慎支持JS功能,避免不必要的麻烦。

提到对于Android4.2以下的JS任意代码执行漏洞,Android4.2以下?不必支持了吧!

2. 请使用https的链接,第一是安全;第二是避免被恶心的运营商劫持,插入广告,影响用户体验。

3. 处理file协议安全漏洞


//若不需支持,则直接禁止 file 协议setAllowFileAccess(false);
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);  


4. 密码明文保存漏洞


由于webView默认开启密码保存功能,所以在用户输入密码时,会弹出提示框,询问用户是否保存。若选择保存,则密码会以明文形式保存到 /data/data/com.package.name/databases/webview.db中,这样就有被盗取密码的危险。所以我们应该禁止网页保存密码,设置WebSettings.setSavePassword(false)


5. 开启安全浏览模式

<manifest>
   <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
          android:value="true" />

   <application>
    ...
   </application>
</manifest>


启用安全浏览模式后,WebView 将参考安全浏览的恶意软件和钓鱼网站数据库检查访问的 URL ,在用户打开之前给予危险提示,体验类似于Chrome浏览器。


4数据存储安全


1. 秘钥及敏感信息


此类配置应当妥善存放,不要在类中硬编码敏感信息,可以使用JNI将敏感信息写到Native层。


2. SharePreferences


首先不应当使用SharePreferences来存放敏感信息。存储一些配置信息时也要配置好访问权限,如私有的访问权限 MODE_PRIVATE,避免配置信息被篡改。


3. 签名配置signingConfigs


避免明文保存签名密码,可以将密码保存到本地,无需上传版本控制系统

在app目录下建立一个不加入版本控制系统的gradle.properties文件:

STORE_PASSWORD = qwer1234
KEY_PASSWORD = demo1234
KEY_ALIAS = demokey


gradle将自动引入gradle.properties文件,可以直接在buld.gradle文件中使用:


signingConfigs {
release {
    try {
        storeFile file("E:\FDM\Key\demo.jks")
        storePassword STORE_PASSWORD
        keyAlias KEY_ALIAS
        keyPassword KEY_PASSWORD
    }catch (ex) {
        throw new InvalidUserDataException("You should define KEYSTORE_PASSWORD and KEY_PASSWORD in gradle.properties.")
    }
}
}


5数据传输安全


1. 使用HTTPS协议


HTTPS的主要思想是在不安全的网络上创建一安全信道,并可在使用适当的加密包和服务器证书可被验证且可被信任时,对窃听和中间人攻击提供合理的防护。可以说是非常基础的安全防护级别了。


2. Android网络安全性配置


该特性让应用可以在一个安全的声明性配置文件中灵活的自定义其网络安全设置,而无需修改应用代码,满足更高的安全性要求。


在AndroidManifest.xml中配置

networkSecurityConfig:


<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
   <application android:networkSecurityConfig="@xml/network_security_config"
                   ... >

       ...
   </application>
</manifest>


network_security_config文件如下:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
   <domain-config>
       <domain includeSubdomains="true">example.com</domain>
       <trust-anchors>
           <certificates src="@raw/my_ca"/>
       </trust-anchors>
   </domain-config>
</network-security-config>


以 PEM 或 DER 格式将自签署或非公共 CA 证书添加到 res/raw/my_ca。这让我回想起年前听到渗透测试结果时的恐惧,好家伙,自己装了个证书...


这部分更多细节、功能请参考官方文档网络安全配置

https://developer.android.com/training/articles/security-config.html?hl=zh-cn


6其他安全问题


1. 日志输出


日志是我们开发调试中不可或缺的一部分,但也是最容易泄露敏感信息的地方。所以,在我们发布应用时,应当关闭、甚至移除Log输出。


2. 混淆、加固


混淆代码,可以增加反编译破解的难度。但是我们在使用混淆功能时也要注意实体类、与JS交互的方法、第三方混淆配置等问题。


应用加固也是近年来比较热门的应用安全解决方案,各大厂商都有自己的加固方案,常见的如腾讯乐固、360加固等等。


3. 漏洞检测工具


当项目代码量庞大以后,积累了较多的历史代码,人工检测代码工作量大。这时,漏洞检查工具就派上用场了。中文的漏洞检测工具中比较有名的就是360的FireLine。


参考资料:

Security for Android Developers

https://developer.android.com/topic/security/index.html

Android Intent Scheme URLs攻击

https://blog.csdn.net/l173864930/article/details/36951805

您的 Java 代码安全吗 — 还是暴露在外?

https://www.ibm.com/developerworks/cn/java/j-staticsec/#23

android WebView详解,常见漏洞详解和安全源码

https://juejin.im/post/58a037df86b599006b3fade4



推荐阅读

我和编程在一起的第三年:Android总结

ViewPager 刷新无效?



如果你想要跟大家分享你的文章,欢迎投稿~

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存