这两天阅读了一下android developer网站与安全相关的部分,对android编程过程中主要注意的安全细节进行了学习,在此总结一下要点。
Android框架自身的安全性机制
- Android应用程序沙盒,把每个app的数据与代码都分隔开。
- 对各种安全功能进行了强健实现的应用程序框架,如密码学、权限、安全IPC等。
- 加密的文件系统,它能够保护丢失或被盗设备的数据。
- 用户能够授权权限来限制对系统和用户数据的访问。
- 应用程序定义的权限。
数据存储
使用内部存储
- 默认情况下,你在内部存储上创建的文件只能被该app本身访问。
- 应该尽量避免在IPC文件上使用 MODE_WORLD_WRITEABLE或MODE_WORLD_READABLE模式。如果实在需要数据共享,应该使用content provider。
- 对于敏感数据,应该选择一个密钥对本地文件进行加密,这个密钥应该不能直接被应用程序访问。比如,把密钥保存在KeyStore中,并且用一个不存储在该设备上的用户密码来保护它。
使用扩展存储
- 扩展存储(如SD卡)上的文件是全局可读写的(被其他应用程序)。不要在扩展存储上存放敏感信息。
- 处理扩展存储上的数据前最好进行输入验证。
使用content providers
- 如果不打算让其他应用程序访问你的ContentProvider,就在manifest文件中用
android:exported=false
来标记它。 - 如果创建的ContentProvider需要被提供给其他应用程序使用,可以特别指定单独的读权限和写权限。
- 如果使用ContentProvider只是在你自己的几个app之间共享数据,那么最好把
android:protectionLevel
属性设置为"signature"
。 - 可以对content provider提供更细粒度的访问控制,如使用
android:grantUriPermissions
属性,在Intent对象中使用FLAG_GRANT_READ_URI_PERMISSION
和FLAG_GRANT_WRITE_URI_PERMISSION
标记。 - 在访问content provider时,使用参数化查询方法如query()、update()、delete()等来避免潜在的SQL注入风险。但这仍有风险,比如selection参数是通过之前用户提交的数据连接而成的。
- 不要对写权限的安全性产生错觉。比如,如果写权限允许写SQL语句,那么WHERE字句巧妙地构造就可以使得“写权限”等价于“读写权限”。
使用权限
请求权限
- 请求最少数量的权限,如果一个权限对你的app来说不是必须的,就不要请求。
- 完全有可能用一种方法不请求任何权限,比如为了创建独一无二的标示符可以使用GUID而不是请求设备信息,可以把数据存储在内部存储(无须权限)而不是存储在扩展存储(需要权限)。
- 推荐尽可能使用访问控制,而不是用户确认的权限,因为权限可能让用户疑惑。
- 不要泄露有权限保护的数据。
创建权限
这种情况很少见,因为系统定义的权限已近覆盖了大部分的需求。
如果必须创建新权限,考虑是否可以用"signature"
级别的protection level
来实现。
使用网络
使用IP网络
- 确保对于敏感信息使用合适的协议,比如HttpsURLConnection。如果服务器支持的话,我们更倾向于使用HTTPS而不是HTTP,因为移动设备频繁的连接在不安全的网络中,比如公共Wi-Fi热点。
- 认证的加密的套接字级别的会话,可以很容易地用SSLSocket类来实现。
- 有些引用程序使用localhost网络端口来处理敏感的IPC,这种做法应该避免,因为其他应用程序也能够访问到这些接口。应该使用具有授权控制的Android IPC机制比如Service。
- 所有通过HTTP协议或其他不安全协议获取到的数据都是不可信任的,要做好数据验证工作。
使用电话网络
SMS协议的设计本就不适合app传输数据。不管是在网络上还是设备上,SMS既没有加密也没有强壮的认证。在Android设备上,SMS消息是以广播形式传输的,所以它们可以被所有其他具有READ_SMS
权限的应用程序读取或捕获。
使用WebView
- 如果你的应用程序不直接通过webview使用JavaScript,那么就不要调用
setJavaScriptEnabled()
。默认情况下webview是不执行JavaScript的,这样就可以防止跨站脚本攻击。 - 只把
addJavaScriptInterface()
暴露给具有可信输入的网页。 - 如果你的应用程序通过WebView访问敏感数据,你可以使用
clearCache()
方法来删除任何存储在本地的文件。也可以使用服务器端的header比如no-cache
来表明一个应用程序不应该缓存特定的内容。 - 用户名和密码不应该存储在设备上。初次认证的时候使用用户提供的用户名和密码,以后就使用一个短生命周期的、service特有的认证token。
- 被多个应用程序访问的Service应该使用AccountManager来访问。
使用密码学
- 如果需要安全地从已知位置获取一个文件,可以很简单地使用
HTTPS URI
。 - 如果需要安全数据通道,考虑使用
HttpsURLConnection
或者SSLSocket
,而不是去写一个你自己的协议。 - 如果你真的需要实现自己的协议,也不要自己实现加密算法,可以直接只用存在的已实现的加密算法,比如
Cipher
类中的AES或者RSA算法。 - 使用安全随机数生成器
SecureRandom
来初始化任何的密钥、KeyGenerator
。使用非安全随机数生成器得到的密钥,会削弱算法的安全强度,也会导致离线攻击。 - 如果需要存储一个密钥来提供多次使用,应该使用诸如
KeyStore
之类的机制。
使用IPC
- 不要使用传统Linux中的IPC技术比如网络套接字和共享文件等。应该使用Android系统提供的IPC功能比如Intent、与Service配合的Binder或者Messenger、BroadcastReceiver。Android的IPC机制允许你验证连接到你IPC的应用程序的身份,并且能够为每个IPC机制设置安全策略。
- 如果你的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