现在很流行android和H5混合开发,大大节约了成本,但是也有很多我们不得不考虑的安全问题。
主要问题有三类
- 任意代码执行漏洞
- 密码明文存储漏洞
- 域控制不严格漏洞
1.1 WebView 任意代码执行漏洞
11.1 addJavascriptInterface 接口引起远程代码执行漏洞
首先我们要明确任何和支付有关的操作最好不要通过js交互进行。
给大家看看攻击的js核心代码
function execute(cmdArgs)
{
// 步骤1:遍历 window 对象
// 目的是为了找到包含 getClass ()的对象
// 因为Android映射的JS对象也在window中,所以肯定会遍历到
for (var obj in window) {
if ("getClass" in window[obj]) {
// 步骤2:利用反射调用forName()得到Runtime类对象
alert(obj);
return window[obj].getClass().forName("java.lang.Runtime")
// 步骤3:以后,就可以调用静态方法来执行一些命令,比如访问文件的命令
getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
// 从执行命令后返回的输入流中得到字符串,有很严重暴露隐私的危险。
// 如执行完访问文件的命令之后,就可以得到文件名的信息了。
}
}
}
不过不用担心,Google 在Android 4.2 版本中规定对被调用的函数以 @JavascriptInterface进行注解从而避免漏洞攻击。而4.2之前采用拦截prompt()进行漏洞修复。(这里我就不说4.2之前的解决办法了)
1.2 密码明文存储漏洞
WebView默认开启密码保存功能 :
mWebView.setSavePassword(true)`
开启后,在用户输入密码时,会弹出提示框:询问用户是否保存密码;
如果选择”是”,密码会被明文保到 /data/data/com.package.name/databases/webview.db 中,这样就有被盗取密码的危险
解决办法:关闭密码保存提醒
WebSettings.setSavePassword(false)
1.3 域控制不严格漏洞
即 A 应用可以通过 B 应用导出的 Activity 让 B 应用加载一个恶意的 file 协议的 url,从而可以获取 B 应用的内部私有文件,从而带来数据泄露威胁。
setAllowFileAccess()
// 设置是否允许 WebView 使用 File 协议
webView.getSettings().setAllowFileAccess(true);
// 默认设置为true,即允许在 File 域下执行任意 JavaScript 代码
使用 file 域加载的 js代码能够使用进行同源策略跨域访问,从而导致隐私信息泄露
同源策略跨域访问:对私有目录文件进行访问
针对 IM 类产品,泄露的是聊天信息、联系人等等
针对浏览器类软件,泄露的是cookie 信息泄露。
解决办法1:禁用 file 协议;
webView.getSettings().setAllowFileAccess(false);
这样虽然最方便,但同时也限制了 WebView 的功能,使其不能加载本地的 html 文件。
解决办法2:对于需要使用 file 协议的应用,禁止 file 协议加载 JavaScript。;
setAllowFileAccess(true);
// 禁止 file 协议加载 JavaScript
if (url.startsWith("file://") {
setJavaScriptEnabled(false);
} else {
setJavaScriptEnabled(true);
}
setAllowFileAccessFromFileURLs()
// 设置是否允许通过 file url 加载的 Js代码读取其他的本地文件
webView.getSettings().setAllowFileAccessFromFileURLs(true);
// 在Android 4.1前默认允许
// 在Android 4.1后默认禁止
这样不安全,解决办法直接关掉
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs()
// 设置是否允许通过 file url 加载的 Javascript 可以访问其他的源(包括http、https等源)
webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
// 在Android 4.1前默认允许(setAllowFileAccessFromFileURLs()不起作用)
// 在Android 4.1后默认禁止
这个一般还好,如果想保证资源的安全的话,建议关掉
setAllowUniversalAccessFromFileURLs(false);
setJavaScriptEnabled()
// 设置是否允许 WebView 使用 JavaScript(默认是不允许)
webView.getSettings().setJavaScriptEnabled(true);
// 但很多应用(包括移动浏览器)为了让 WebView 执行 http 协议中的 JavaScript,都会主动设置为true,不区别对待是非常危险的。
所以建议最终的配置方案是:
对于不需要使用 file 协议的应用,禁用 file 协议
// 禁用 file 协议;
setAllowFileAccess(false);
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);
对于不需要使用 file 协议的应用,禁用 file 协议
// 需要使用 file 协议
setAllowFileAccess(true);
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);
// 禁止 file 协议加载 JavaScript
if (url.startsWith("file://") {
setJavaScriptEnabled(false);
} else {
setJavaScriptEnabled(true);
}
最后,还是要提醒一句,setJavaScriptEnabled这个方法调用就会很不安全,最好不要进行支付或者传一些安全相关的数据。(——。——)
对了最后给出封装的webview
package com.example.admin.tbswebview;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AbsoluteLayout;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import com.tencent.smtt.export.external.interfaces.SslError;
import com.tencent.smtt.export.external.interfaces.SslErrorHandler;
import com.tencent.smtt.sdk.WebChromeClient;
import com.tencent.smtt.sdk.WebSettings;
import com.tencent.smtt.sdk.WebView;
import com.tencent.smtt.sdk.WebViewClient;
/**
* Created by ls on 2017/6/25.
*/
public class ProgressWebview extends WebView {
private ProgressBar progressbar; //进度条
private int progressHeight = 10; //进度条的高度,默认10px
public ProgressWebview(Context context) {
super(context);
initView(context);
}
public ProgressWebview(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
initView(context);
}
private void initView(Context context) {
//开启js脚本支持
getSettings().setJavaScriptEnabled(true);
//创建进度条
progressbar = new ProgressBar(context, null,
android.R.attr.progressBarStyleHorizontal);
//设置加载进度条的高度
progressbar.setLayoutParams(new AbsoluteLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, progressHeight, 0, 0));
Drawable drawable = context.getResources().getDrawable(R.drawable.progressbar_loading);
progressbar.setProgressDrawable(drawable);
//添加进度到WebView
addView(progressbar);
//适配手机大小
getSettings().setUseWideViewPort(true);
getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
getSettings().setLoadWithOverviewMode(true);
getSettings().setSupportZoom(true);
getSettings().setBuiltInZoomControls(true);
getSettings().setDisplayZoomControls(false);
/**
* 安全相关配置
*/
getSettings().setAllowFileAccess(true);
getSettings().setAllowFileAccessFromFileURLs(false);
getSettings().setAllowUniversalAccessFromFileURLs(false);
setWebChromeClient(new WVChromeClient());
setWebViewClient(new WVClient());
}
//进度显示
private class WVChromeClient extends WebChromeClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (newProgress == 100) {
progressbar.setProgress(100);
new Handler().postDelayed(new Runnable(){
public void run() {
progressbar.setVisibility(View.GONE);
progressbar.setProgress(0);
}
}, 300);
} else {
if (progressbar.getVisibility() == GONE)
progressbar.setVisibility(VISIBLE);
progressbar.setProgress(newProgress);
}
if (mListener != null) {
mListener.onProgressChange(view, newProgress);
}
super.onProgressChanged(view, newProgress);
}
}
private class WVClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//在当前Activity打开
view.loadUrl(url);
// 安全设置setAllowFileAccess(true)就需要,禁止 file 协议加载 JavaScript
if (url.startsWith("file://")){
getSettings().setJavaScriptEnabled(false);
} else {
getSettings().setJavaScriptEnabled(true);
}
return true;
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
//https忽略证书问题
handler.proceed();
}
@Override
public void onPageFinished(WebView view, String url) {
// progressbar.setVisibility(GONE);
if (mListener != null) {
mListener.onPageFinish(view);
}
super.onPageFinished(view, url);
}
}
private onWebViewListener mListener;
public void setOnWebViewListener(onWebViewListener listener) {
this.mListener = listener;
}
//进度回调接口
public interface onWebViewListener {
void onProgressChange(WebView view, int newProgress);
void onPageFinish(WebView view);
}
}
饿饿,如果你找progressbar_loading.xml就去看看我另一篇封装的
这里写链接内容