WebView的优化--处理WebView的容易忽略的漏洞

现在很流行android和H5混合开发,大大节约了成本,但是也有很多我们不得不考虑的安全问题。
主要问题有三类

  1. 任意代码执行漏洞
  2. 密码明文存储漏洞
  3. 域控制不严格漏洞

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就去看看我另一篇封装的
这里写链接内容

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值