安全登录
在Andorid登录过程中有些信息会很常用:
用户名和密码
设备信息,如DeviceID和AndroidID
网络信息,如IP地址
为了下一次登录方便,登录过程中会记住用户名,这意味着用户名将保存在设备文件系统的某个位置。
用户验证的最佳实践:
- 不要缓存密码
- 限定最短的密码长度
- 验证Email地址
- 多步验证
- 服务端和客户端都进行验证
不要缓存密码
不要保存或缓存用户名,尤其是密码,因为手机上的信息总有风险被解密,即使对密码进行加密,但如果密钥保存在apk中,那么密码和未加密是一样的。
限定最短的密码长度
少于6位的密码将很容易被爆力破解。
验证Email地址
因为你以后很可能需要用到,可通过正则表达式,也可在注册过程中发送Email,通过Email中的链接验证,最好两种方式结合使用。
多步验证
拿到设备ID、IP地址以及地理位置信息或短信发送额外PIN码到手机上来作为额外的验证信息。
如果要保存用户隐私信息,应该使用非对称加密。这就需要服务器(私钥保存在服务器上)来对数据进行解密,从而增加一次客户端服务端的交互,这种做法才能保证用户数据的安全。
验证流程的补充信息
AndroidID是一个64位的数字或者是设备第一次启动的时候随机生成的UUID,在设备的正常使用中应保持一致。
DeviceID,只支持WIFI的设备在telephony硬件上根本没有DeviceID,恢复出厂设置后DeviceID不会发生变化。获取DeviceID需要 READ_PHONE_STATE 权限。
加密用户密码
在用户第一次登录的时候要输入用户名和密码,使用Keyczar非对称加密密码,公钥放在客户端,私钥保存在服务器上。
保持登录状态
要为应用选择一个合适的session超时时间,财务或健康相关的,用户在几分钟内没有操作就应该让session过期,如果是游戏应用session可以维持数天。
网络通信
第三方API提供商使用API key来作为一个简单的校验,为避免API key被人破解滥用,需要把它隐藏起来避免被人找到。
加密方案
对称加密
使用同一个key对数据进行加密和解密。
非对称加密
使用公钥和私钥在客户端和服务端之间传输数据。
中间人攻击示例
使用Cydia Impactor 来 root手机步骤
- 确保打开开发者选项
- 开启usb调试
- 打开Cydia Impactor应用,点击开始按钮
- 在命令行输入adb shell,再输入su,如果看到#符号说明root成功
在root掉的手机上安装ProxyDroid,用它来把通信转发到计算机上,然后再计算机上安装Charlse Proxy来查看网络通信。
使用Charlse Proxy创建出自签名的证书。
当使用HTTPS连接发送请求,如果没有正确处理证书相关的内容(忽略了SSL相关的错误),就会看到未加密的通信,代码正常工作HTTPS会请求失败,显示 SSL证书被拒绝了(no peer certificate) 的错误信息。
小结:关键思想是任何的个人信息都应该把这些数据加密,尽最大可能把客户数据保存在服务器而不是手机上,如果必须用保存在手机上,如API key,非对称加密可以确保数据安全。
Android数据库
应用的数据库文件在/data/data/packagename/databases目录下。
备份应用的数据库
使用命令备份数据库
adb backup packagename
备份是定制文件头的tar文件,需下载Android Backup Extrator把它转换成tar格式,转换备份的backup.ab文件
java -jar abe.jar unpack backup.ab backup.tar
使用tar -xvf或者Windows上的7zip解压tar文件,就可以看到里面的数据库了。
阻止备份
在 manifest 文件中设置 android:allowBackup="true" 。这可以禁止adb backup备份数据库,连系统备份也不行。但是root了的手机可以访问到数据库,运行 chmod 777 <文件名> 来完全放开文件权限后,可以使用adb pull 把数据库从手机上拉取下来。
SQLCipher
SQLCipher 是和SQLite3一起使用的一个开源库,可用来加密数据库中的数据,用它在openOrCreateDatabase时需要一个密码参数。
隐藏密钥
- 每一次都请求密钥
密钥保存在用户的脑袋里,而不是手机中。
- 在 Shared Preferences 中隐藏密钥
有两种不同的做法:在用户第一次打开应用的时候要求输入密钥,然后保存在Shared Preferences文件中。另一种是在应用第一次打开的时候直接加载指定用户名和密码。Android会从 resources/xml 目录下加载数据,并把它保存到Shared Preferences文件中。
adb shell cat /data/data/packagename/shared_prefs/* 可以查看Shared Preferences文件。
- 在代码中隐藏密钥
使用设备相关的来为单独的设备生成一个key,比如device_id,android_id,或者任何跟特定的手机相关的属性,如Build.ID,Build.MODEL。把这些信息组合起来为单独的手机生成单独的密钥。仅仅比不同的设备上使用相同的密钥稍微安全一点点。
- 在NDK中隐藏密钥
C++不能被反编译,只能反汇编。
使用NDK时,在代码中使用一个十六进制类型的文本作为密钥,在十六进制编辑器里密钥就不会以明文显示出来了。还可以使用设备信息或应用信息的字符串在NDK中生成唯一的应用密钥。
- 使用Web Service保护密钥
最安全的办法是把存储密钥或者说使用算法生成密钥的方法放到远程,然后通过Web Service来访问密钥。缺点是在设备打开数据库的时候需要联网,这对于终端用户可能不可接受。
SQL注入
没有SQL注入保护的登录语句
Cursor cursor = db.rawQuery("select * from login where USERNAME = '" + name + "' and PASSWORD = '" + password + "';", null);
很容易被注入,如:
select * from login where USERNAME = '' OR 1==1 --' and PASSWORD = 'test'
可使用正则表达式检查输入的内容,和使用SQL prepared statements。如用prepared statements来保护代码:
Cursor cursor = db.rawQuery("select * from login where USERNAME = ? and PASSWORD = ?;", new String[]{name, password});
设备安全
adb备份
root 过的手机通过修改文件权限,使用adb pull 可导出文件。
adb backup 应用的/data/data目录下的所有文件都以跟Unix tar类似的格式被备份到电脑上。
可以设置android:allowBackup="false" 关闭掉应用备份。
日志
开发过程中通常会添加一些调试信息,可能有http请求和服务器通信的信息。要避免调试日志信息被打包到生产版本中。
Proguard混淆工具可以用来移除上线应用的所有日志信息。在 proguard-rules.pro 文件中配置
-assumenosideeffects class android.util.Log {
<method>;
}