- 1. 本地文件存储
- 1.1. 禁止全局读写
- 1.2. 数据加密保存
- 2. 安全通信
- 2.1. 信任任意整证书
- 2.2. 解决方法
- 3. 加密
- 3.1. 敏感逻辑实现
- 3.2. 硬编码
- 4. 组件安全
- 4.1. Activity导出
- 4.2. Activity劫持
- 4.3. Service
- 4.4. Provider
- 4.5. Receiver
- 5. WebView组件
- 5.1. Webview File域同源策略绕过
- 5.2. WebView隐藏接口问题
- 5.3. 远程命令执行攻击
- 5.4. WebView绕过证书校验漏洞
- 6. 小结
1. 本地文件存储
eg: 数据库
在程序中如果使用了SharedPreference或者SQLiteDatabase,要查看SharedPreference或SQLiteDatabase是否设置了安全权限
1.1. 禁止全局读写
如下代码是不安全的:
try{
SQLiteDatabase rdb = penOrCreateDatabase("all_r_db",Context.MODE_WORLD_READABLE,null);
SQLiteDatabase wdb = penOrCreateDatabase("all_w_db",Context.MODE_WORLD_WRITEABLE,null,null);
}catch(SQLiteException e){
e.printStackTrace();
}
应改为MODE_PRIVATE模式创建
try{
SQLiteDatabase rdb = penOrCreateDatabase("all_r_db",Context.MODE_PRIVATE,null);
SQLiteDatabase wdb = penOrCreateDatabase("all_w_db",Context.MODE_PRIVATE,null,null);
}catch(SQLiteException e){
e.printStackTrace();
}
1.2. 数据加密保存
敏感数据需要加密后保存,比如身份证号、手机号码、信用卡号、银行卡号等
2. 安全通信
eg:网络通信
2.1. 信任任意整证书
如下代码中程序中有重写TrustManager的证书验证方法,而且checkServerTrusted方法没有真正实现服务器端的证书校验而且代码中这样写:httpsURLConnection.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);,会导致默认信任所有主机。verify方法没有实现校验服务器证书域名是否相符的功能
public static String getUnSafeFromServer (){
String response="";
final String https_url ="https://XXXX/";
new AsyncTask(){
@Override
protected Boolean doInBackground (String ... Param){
try{
TrustManager [] trustAllCerts = new TrustManager[] {
new X509TrustManager (){
@Override
public void checkClientTrusted (X509Certificate[] X509Certificates,String s) throws CertificateException {
}
@Override
public void checkServerTrusted (X509Certificate[] X509Certificates,String s) throws CertificateException{
}
@Override
public X509Certificate[] getAcceptedIssuers(){
return new X509Certificate[0];
}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null,trustAllCerts,null);
URL url = new URL(https_url);
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
httpsURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());
httpsURLConnection.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
final HostnameVerifier hostnameVerifier = new HostnameVerifier(){
@Override
public boolean verify(String s ,SSLSession sslsession){
return true;
}
};
.......
}
2.2. 解决方法
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
for (X509Certificate cert : chain) {
// Make sure that it hasn't expired.
cert.checkValidity();
// Verify the certificate's public key chain.
try {
cert.verify(((X509Certificate) ca).getPublicKey());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
}
}
}
3. 加密
3.1. 敏感逻辑实现
设计要求,需查看工程中是否有使用Native实现的关键敏感逻辑程序
3.2. 硬编码
加密密钥写在了代码里,如果应用被反编译了,很容易导致加密被破解,造成敏感信息泄露
4. 组件安全
安卓平台四大组件Activity,Service,Provider,Receiver均有exported属性设置。
4.1. Activity导出
对于Activity组件,如果有intent filter设置的话,exported默认为true,必须关闭。如果没有则可以不关注是否设置了该Activity组件exported为false,因为默认是false。如果Activity组件有Intent-filter配置,例如XML中
则该Activity组件的相关配置是不安全的,如下配置才是安全的:
举个栗子:2014年的时候华为网盘android客户端本地密码绕过:
启动设置本地的密码的activity:com.huawei.dbank.v7.ui.setting.LocalPassWordInitActivity,即可重置本地密码
4.2. Activity劫持
用户打开安卓手机上的某一应用,进入到登陆页面,这时恶意软件侦测到用户的这一动作,立即弹出一个与该应用界面相同的Activity,覆盖掉了合法的Activity
在建立的正常Activity的登陆界面(也就是MainActivity)中重写onKeyDown方法和onPause方法,这样一来,当其被覆盖时,就能够弹出警示信息
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
//判断程序进入后台是否是用户自身造成的(触摸返回键或HOME键),是则无需弹出警示。
if((keyCode==KeyEvent.KEYCODE_BACK || keyCode==KeyEvent.KEYCODE_HOME) && event.getRepeatCount()==0){
needAlarm = false;
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onPause() {
//若程序进入后台不是用户自身造成的,则需要弹出警示
if(needAlarm) {
//弹出警示信息
Toast.makeText(getApplicationContext(), "您的登陆界面被覆盖,请确认登陆环境是否安全", Toast.LENGTH_SHORT).show();
//启动我们的AlarmService,用于给出覆盖了正常Activity的类名
Intent intent = new Intent(this, AlarmService.class);
startService(intent);
}
super.onPause();
}
4.3. Service
对于Service组件,与Activity组件类似,要检查如果有intent filter设置的话,exported默认为true,必须关闭。具体代码示例与验证规则与Activity一致。
4.4. Provider
对于Provider组件,根据使用的Android SDK版本而定,如果版本为16(安卓4.1)或以下版本,则必须有exported的属性设置为false,如果是17(安卓4.2)以上版本则不必须
4.5. Receiver
对于Receiver组件,与Activity组件一致,主要查看是否有Intent filter,如果有则必须检查是否设置该属性为false
5. WebView组件
5.1. Webview File域同源策略绕过
可造成cookie等敏感信息泄露,黑客可利用该漏洞获取FireFox的所有文件。低版本的系统Webview组件也存在同样的漏洞,应用程序使用Webview并支持File域,就会受到该漏洞的攻击
如果程序中有使用WebView,在设置WebView建议关闭file协议:
mWebView.setAllowFileAccess(false)
setAllowFileAccessFromFileURLs(false)
setAllowUniversalAccessFromFileURLs(false)
如果必须使用,则应该禁止调用javascript
webView.getSettings. setJavaScriptEnabled(false);
eg:WebView允许js访问本地文件,并且允许js运行,则可以上传一个含有js代码的文件(代码内容为延迟读取自身)并将目标WebView启动让其读取上传的文件,然后将文件删掉并创建一个软链接到目标APP私有文件,则在延迟时间过后,WebView就会显示读取到的私有文,达成了攻击目的
5.2. WebView隐藏接口问题
如果使用Android4.4之前版本程序使用WebView,需要移除searchBoxJavaBridge_、accessibility、accessibilityTraversal危险接口。这三个接口存在远程代码执行的威胁
5.3. 远程命令执行攻击
13年下半年,Webview曝出了比较严重的远程命令执行漏洞。在webView 下有一个非常特殊的接口函数addJavascriptInterface。能实现本地java和js的交互。利用addJavascriptInterface这个接口函数可实现反射穿透webkit控制android 本机,比如向本机写入文件。技术细节可参考如下文章:《WebView中接口隐患与手机挂马利用》
处理方法
1、不要将不必要组件导出。
2、如需导出,禁止使用File域。
3、如需使用File协议,禁止js执行:setJavaScriptEnabled(False)
5.4. WebView绕过证书校验漏洞
禁止使用proceed()函数忽略证书错误,应该抛给系统进行安全警告。当SSL错误处理时,使用了proceed方法。造成假如SSL证书错误仍然可以访问
6. 小结
- 本地文件存储:
对于敏感数据本地存储的要求,其实可以总结为:能不存就不存,非要存的话一定要加密。加密密钥不要硬编码到代码中,最好是获取每个终端的特有信息,如IMEI、mac之类 - 安全通信
1.真正实现TrustManager
2.禁止使用AllowAllHostnameVerifier和ALLOWALLHOSTNAMEVERIFIER实例化,应该使用STRICTHOSTNAME_VERIFIER严格校验。
3.禁止使用SSLCertificateSocketFactory;->getInsecure()函数禁用所有SSL安全校验。
4.禁止使用proceed()函数忽略证书错误,应该抛给系统进行安全警告。 - 安卓四大组件:
如非必要,exported可以都显示的设置为false,在应用进入后台运行时要弹出提示信息(目前看来这个都有做了,基本上所有银行都会有这个功能),不要在本地做密码验证 - WebView:
1、禁止使用addJavascriptInterface方法
2、设置WebView建议关闭file协议,mWebView.setAllowFileAccess(false)setAllowFileAccessFromFileURLs(false),setAllowUniversalAccessFromFileURLs(false)
3、移除危险方法removeJavascriptInterface("searchBoxJavaBridge_")、removeJavascriptInterface("accessibility")、removeJavascriptInterface("accessibilityTraversal")
4、使用handler.cancel()