Android编程要注意的安全规范

这两天阅读了一下android developer网站与安全相关的部分,对android编程过程中主要注意的安全细节进行了学习,在此总结一下要点。

Android框架自身的安全性机制

  1. Android应用程序沙盒,把每个app的数据与代码都分隔开。
  2. 对各种安全功能进行了强健实现的应用程序框架,如密码学、权限、安全IPC等。
  3. 加密的文件系统,它能够保护丢失或被盗设备的数据。
  4. 用户能够授权权限来限制对系统和用户数据的访问。
  5. 应用程序定义的权限。

    数据存储

    使用内部存储

  6. 默认情况下,你在内部存储上创建的文件只能被该app本身访问。
  7. 应该尽量避免在IPC文件上使用 MODE_WORLD_WRITEABLE或MODE_WORLD_READABLE模式。如果实在需要数据共享,应该使用content provider。
  8. 对于敏感数据,应该选择一个密钥对本地文件进行加密,这个密钥应该不能直接被应用程序访问。比如,把密钥保存在KeyStore中,并且用一个不存储在该设备上的用户密码来保护它。

使用扩展存储

  1. 扩展存储(如SD卡)上的文件是全局可读写的(被其他应用程序)。不要在扩展存储上存放敏感信息。
  2. 处理扩展存储上的数据前最好进行输入验证。

使用content providers

  1. 如果不打算让其他应用程序访问你的ContentProvider,就在manifest文件中用android:exported=false来标记它。
  2. 如果创建的ContentProvider需要被提供给其他应用程序使用,可以特别指定单独的读权限和写权限。
  3. 如果使用ContentProvider只是在你自己的几个app之间共享数据,那么最好把android:protectionLevel属性设置为"signature"
  4. 可以对content provider提供更细粒度的访问控制,如使用android:grantUriPermissions属性,在Intent对象中使用FLAG_GRANT_READ_URI_PERMISSIONFLAG_GRANT_WRITE_URI_PERMISSION标记。
  5. 在访问content provider时,使用参数化查询方法如query()、update()、delete()等来避免潜在的SQL注入风险。但这仍有风险,比如selection参数是通过之前用户提交的数据连接而成的。
  6. 不要对写权限的安全性产生错觉。比如,如果写权限允许写SQL语句,那么WHERE字句巧妙地构造就可以使得“写权限”等价于“读写权限”。

使用权限

请求权限

  1. 请求最少数量的权限,如果一个权限对你的app来说不是必须的,就不要请求。
  2. 完全有可能用一种方法不请求任何权限,比如为了创建独一无二的标示符可以使用GUID而不是请求设备信息,可以把数据存储在内部存储(无须权限)而不是存储在扩展存储(需要权限)。
  3. 推荐尽可能使用访问控制,而不是用户确认的权限,因为权限可能让用户疑惑。
  4. 不要泄露有权限保护的数据。

创建权限

这种情况很少见,因为系统定义的权限已近覆盖了大部分的需求。
如果必须创建新权限,考虑是否可以用"signature"级别的protection level来实现。

使用网络

使用IP网络

  1. 确保对于敏感信息使用合适的协议,比如HttpsURLConnection。如果服务器支持的话,我们更倾向于使用HTTPS而不是HTTP,因为移动设备频繁的连接在不安全的网络中,比如公共Wi-Fi热点。
  2. 认证的加密的套接字级别的会话,可以很容易地用SSLSocket类来实现。
  3. 有些引用程序使用localhost网络端口来处理敏感的IPC,这种做法应该避免,因为其他应用程序也能够访问到这些接口。应该使用具有授权控制的Android IPC机制比如Service。
  4. 所有通过HTTP协议或其他不安全协议获取到的数据都是不可信任的,要做好数据验证工作。

使用电话网络

SMS协议的设计本就不适合app传输数据。不管是在网络上还是设备上,SMS既没有加密也没有强壮的认证。在Android设备上,SMS消息是以广播形式传输的,所以它们可以被所有其他具有READ_SMS权限的应用程序读取或捕获。

使用WebView

  1. 如果你的应用程序不直接通过webview使用JavaScript,那么就不要调用setJavaScriptEnabled()。默认情况下webview是不执行JavaScript的,这样就可以防止跨站脚本攻击。
  2. 只把addJavaScriptInterface()暴露给具有可信输入的网页。
  3. 如果你的应用程序通过WebView访问敏感数据,你可以使用 clearCache()方法来删除任何存储在本地的文件。也可以使用服务器端的header比如no-cache来表明一个应用程序不应该缓存特定的内容。
  4. 用户名和密码不应该存储在设备上。初次认证的时候使用用户提供的用户名和密码,以后就使用一个短生命周期的、service特有的认证token。
  5. 被多个应用程序访问的Service应该使用AccountManager来访问。

使用密码学

  1. 如果需要安全地从已知位置获取一个文件,可以很简单地使用HTTPS URI
  2. 如果需要安全数据通道,考虑使用HttpsURLConnection或者SSLSocket,而不是去写一个你自己的协议。
  3. 如果你真的需要实现自己的协议,也不要自己实现加密算法,可以直接只用存在的已实现的加密算法,比如Cipher类中的AES或者RSA算法。
  4. 使用安全随机数生成器SecureRandom来初始化任何的密钥、KeyGenerator。使用非安全随机数生成器得到的密钥,会削弱算法的安全强度,也会导致离线攻击。
  5. 如果需要存储一个密钥来提供多次使用,应该使用诸如KeyStore之类的机制。

使用IPC

  1. 不要使用传统Linux中的IPC技术比如网络套接字和共享文件等。应该使用Android系统提供的IPC功能比如Intent、与Service配合的Binder或者Messenger、BroadcastReceiver。Android的IPC机制允许你验证连接到你IPC的应用程序的身份,并且能够为每个IPC机制设置安全策略。
  2. 如果你的IPC机制不打算给其他应用程序使用,可以在manifest清单中把android:exported属性设置为false

其他安全性相关问题,可以参考:
1、http://developer.android.com/training/best-security.html
2、https://www.owasp.org/index.php/OWASP_Mobile_Security_Project#tab=Secure_Mobile_Development

文章目录
  1. 1. Android框架自身的安全性机制
  2. 2. 数据存储
    1. 2.1. 使用内部存储
    2. 2.2. 使用扩展存储
    3. 2.3. 使用content providers
  3. 3. 使用权限
    1. 3.1. 请求权限
    2. 3.2. 创建权限
  4. 4. 使用网络
    1. 4.1. 使用IP网络
    2. 4.2. 使用电话网络
  5. 5. 使用WebView
  6. 6. 使用密码学
  7. 7. 使用IPC